tor-browser

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

keeping-attributes-at-joining-elements.tentative.html (43393B)


      1 <!doctype html>
      2 <meta chareset="utf-8">
      3 <meta name="timeout" content="long">
      4 <meta name="variant" content="?method=backspace">
      5 <meta name="variant" content="?method=forwarddelete">
      6 <title>Not merging attributes at joining elements in contenteditable</title>
      7 <script src="/resources/testharness.js"></script>
      8 <script src="/resources/testharnessreport.js"></script>
      9 <script src="/resources/testdriver.js"></script>
     10 <script src="/resources/testdriver-vendor.js"></script>
     11 <script src="/resources/testdriver-actions.js"></script>
     12 <script src="../include/editor-test-utils.js"></script>
     13 </style>
     14 <div contenteditable></div>
     15 <script>
     16 "use strict";
     17 
     18 const testingBackspace =
     19  new URLSearchParams(document.location.search).get("method") == "backspace";
     20 const caretForBackSpace = testingBackspace ? "[]" : "";
     21 const caretForForwardDelete = testingBackspace ? "" : "[]";
     22 document.execCommand("defaultParagraphSeparator", false, "div");
     23 const utils =
     24  new EditorTestUtils(document.querySelector("div[contenteditable]"));
     25 
     26 // DO NOT USE multi-line comment in this file, then, you can comment out
     27 // unnecessary tests when you need to attach the browser with a debugger.
     28 
     29 // At joining 2 elements, the attributes shouldn't be merged, and from point of
     30 // view of JS/DOM, both element kept after the join and element deleted from the
     31 // DOM tree should have attributes as-is.
     32 promise_test(async t => {
     33  utils.setupEditingHost(
     34    `<div id="left">abc${caretForForwardDelete}</div><div id="right">${caretForBackSpace}def</div>`
     35  );
     36  const leftNode = document.getElementById("left");
     37  const rightNode = document.getElementById("right");
     38 
     39  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
     40 
     41  test(() => {
     42    assert_equals(
     43      leftNode.getAttribute("id"),
     44      "left",
     45      `The left node should keep having id=left (isConnected=${leftNode.isConnected}, ${t.name})`
     46    );
     47  });
     48  test(() => {
     49    assert_equals(
     50      rightNode.getAttribute("id"),
     51      "right",
     52      `The right node should keep having id=right (isConnected=${rightNode.isConnected}, ${t.name})`
     53    );
     54  });
     55  assert_equals(
     56    leftNode.isConnected ^ rightNode.isConnected,
     57    1,
     58    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
     59  );
     60 }, "Joining <div id=\"left\"> and <div id=\"right\">");
     61 
     62 promise_test(async t => {
     63  utils.setupEditingHost(
     64    `<div class="left">abc${caretForForwardDelete}</div><div class="right">${caretForBackSpace}def</div>`
     65  );
     66  const leftNode = utils.editingHost.querySelector(".left");
     67  const rightNode = utils.editingHost.querySelector(".right");
     68 
     69  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
     70 
     71  test(() => {
     72    assert_equals(
     73      leftNode.getAttribute("class"),
     74      "left",
     75      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
     76    );
     77  });
     78  test(() => {
     79    assert_equals(
     80      rightNode.getAttribute("class"),
     81      "right",
     82      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
     83    );
     84  });
     85  assert_equals(
     86    leftNode.isConnected ^ rightNode.isConnected,
     87    1,
     88    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
     89  );
     90 }, "Joining <div class=\"left\"> and <div class=\"right\">");
     91 
     92 promise_test(async t => {
     93  utils.setupEditingHost(
     94    `<div style="font-size:0.8rem">abc${caretForForwardDelete}</div><div style="font-weight:bold">${caretForBackSpace}def</div>`
     95  );
     96  const leftNode = utils.editingHost.querySelector("div[style]");
     97  const rightNode = leftNode.nextSibling;
     98 
     99  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    100 
    101  // style attribute values shouldn't be touched in this but case, but it's
    102  // okay if the values are not merged.
    103  test(() => {
    104    assert_true(
    105      leftNode.getAttribute("style").includes("font-size"),
    106      `The left node should keep having style attribute containing font-size (style="${leftNode.getAttribute("style")}", ${t.name})`
    107    );
    108    assert_false(
    109      leftNode.getAttribute("style").includes("font-weight"),
    110      `The left node should have font-weight in its style attribute (style="${leftNode.getAttribute("style")}", ${t.name})`
    111    );
    112  });
    113  test(() => {
    114    assert_true(
    115      rightNode.getAttribute("style").includes("font-weight"),
    116      `The right node should keep having style attribute containing font-size (style="${rightNode.getAttribute("style")}", ${t.name})`
    117    );
    118    assert_false(
    119      rightNode.getAttribute("style").includes("font-style"),
    120      `The right node should have font-size in its style attribute (style="${rightNode.getAttribute("style")}", ${t.name})`
    121    );
    122  });
    123  assert_equals(
    124    leftNode.isConnected ^ rightNode.isConnected,
    125    1,
    126    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    127  );
    128 }, "Joining <div style=\"font-size:0.8rem\"> and <div style=\"font-weight:bold\">");
    129 
    130 promise_test(async t => {
    131  utils.setupEditingHost(
    132    `<div data-foo="left">abc${caretForForwardDelete}</div><div data-bar="right">${caretForBackSpace}def</div>`
    133  );
    134  const leftNode = utils.editingHost.querySelector("[data-foo=left]");
    135  const rightNode = utils.editingHost.querySelector("[data-bar=right]");
    136 
    137  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    138 
    139  test(() => {
    140    assert_equals(
    141      leftNode.getAttribute("data-foo"),
    142      "left",
    143      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    144    );
    145  });
    146  test(() => {
    147    assert_false(
    148      leftNode.hasAttribute("data-bar"),
    149      `The left node shouldn't have data-bar attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    150    );
    151  });
    152  test(() => {
    153    assert_equals(
    154      rightNode.getAttribute("data-bar"),
    155      "right",
    156      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    157    );
    158  });
    159  test(() => {
    160    assert_false(
    161      rightNode.hasAttribute("data-foo"),
    162      `The right node shouldn't have data-foo attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    163    );
    164  });
    165  assert_equals(
    166    leftNode.isConnected ^ rightNode.isConnected,
    167    1,
    168    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    169  );
    170 }, "Joining <div data-foo=\"left\"> and <div data-bar=\"right\">");
    171 
    172 // Same tests for list-item elements because they may be handled in a different
    173 // path.
    174 promise_test(async t => {
    175  utils.setupEditingHost(
    176    `<ul><li id="left">abc${caretForForwardDelete}</li><li id="right">${caretForBackSpace}def</li></ul>`
    177  );
    178  const leftNode = document.getElementById("left");
    179  const rightNode = document.getElementById("right");
    180 
    181  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    182 
    183  test(() => {
    184    assert_equals(
    185      leftNode.getAttribute("id"),
    186      "left",
    187      `The left node should keep having id=left (isConnected=${leftNode.isConnected}, ${t.name})`
    188    );
    189  });
    190  test(() => {
    191    assert_equals(
    192      rightNode.getAttribute("id"),
    193      "right",
    194      `The right node should keep having id=right (isConnected=${rightNode.isConnected}, ${t.name})`
    195    );
    196  });
    197  assert_equals(
    198    leftNode.isConnected ^ rightNode.isConnected,
    199    1,
    200    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    201  );
    202 }, "Joining <li id=\"left\"> and <li id=\"right\">");
    203 
    204 promise_test(async t => {
    205  utils.setupEditingHost(
    206    `<ul><li class="left">abc${caretForForwardDelete}</li><li class="right">${caretForBackSpace}def</li></ul>`
    207  );
    208  const leftNode = utils.editingHost.querySelector(".left");
    209  const rightNode = utils.editingHost.querySelector(".right");
    210 
    211  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    212 
    213  test(() => {
    214    assert_equals(
    215      leftNode.getAttribute("class"),
    216      "left",
    217      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    218    );
    219  });
    220  test(() => {
    221    assert_equals(
    222      rightNode.getAttribute("class"),
    223      "right",
    224      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    225    );
    226  });
    227  assert_equals(
    228    leftNode.isConnected ^ rightNode.isConnected,
    229    1,
    230    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    231  );
    232 }, "Joining <li class=\"left\"> and <li class=\"right\">");
    233 
    234 promise_test(async t => {
    235  utils.setupEditingHost(
    236    `<ul><li style="font-size:0.8rem">abc${caretForForwardDelete}</li><li style="font-weight:bold">${caretForBackSpace}def</li></ul>`
    237  );
    238  const leftNode = utils.editingHost.querySelector("li[style]");
    239  const rightNode = leftNode.nextSibling;
    240 
    241  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    242 
    243  // style attribute values shouldn't be touched in this but case, but it's
    244  // okay if the values are not merged.
    245  test(() => {
    246    assert_true(
    247      leftNode.getAttribute("style").includes("font-size"),
    248      `The left node should keep having style attribute containing font-size (style="${leftNode.getAttribute("style")}", ${t.name})`
    249    );
    250    assert_false(
    251      leftNode.getAttribute("style").includes("font-weight"),
    252      `The left node should have font-weight in its style attribute (style="${leftNode.getAttribute("style")}", ${t.name})`
    253    );
    254  });
    255  test(() => {
    256    assert_true(
    257      rightNode.getAttribute("style").includes("font-weight"),
    258      `The right node should keep having style attribute containing font-size (style="${rightNode.getAttribute("style")}", ${t.name})`
    259    );
    260    assert_false(
    261      rightNode.getAttribute("style").includes("font-style"),
    262      `The right node should have font-size in its style attribute (style="${rightNode.getAttribute("style")}", ${t.name})`
    263    );
    264  });
    265  assert_equals(
    266    leftNode.isConnected ^ rightNode.isConnected,
    267    1,
    268    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    269  );
    270 }, "Joining <li style=\"font-size:0.8rem\"> and <li style=\"font-weight:bold\">");
    271 
    272 promise_test(async t => {
    273  utils.setupEditingHost(
    274    `<ul><li data-foo="left">abc${caretForForwardDelete}</li><li data-bar="right">${caretForBackSpace}def</li></ul>`
    275  );
    276  const leftNode = utils.editingHost.querySelector("[data-foo=left]");
    277  const rightNode = utils.editingHost.querySelector("[data-bar=right]");
    278 
    279  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    280 
    281  test(() => {
    282    assert_equals(
    283      leftNode.getAttribute("data-foo"),
    284      "left",
    285      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    286    );
    287  });
    288  test(() => {
    289    assert_false(
    290      leftNode.hasAttribute("data-bar"),
    291      `The left node shouldn't have data-bar attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    292    );
    293  });
    294  test(() => {
    295    assert_equals(
    296      rightNode.getAttribute("data-bar"),
    297      "right",
    298      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    299    );
    300  });
    301  test(() => {
    302    assert_false(
    303      rightNode.hasAttribute("data-foo"),
    304      `The right node shouldn't have data-foo attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    305    );
    306  });
    307  assert_equals(
    308    leftNode.isConnected ^ rightNode.isConnected,
    309    1,
    310    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    311  );
    312 }, "Joining <li data-foo=\"left\"> and <li data-bar=\"right\">");
    313 
    314 // Same tests for <dt> elements because they may be handled in a different
    315 // path.
    316 promise_test(async t => {
    317  utils.setupEditingHost(
    318    `<dl><dt id="left">abc${caretForForwardDelete}</dt><dt id="right">${caretForBackSpace}def</dt></dl>`
    319  );
    320  const leftNode = document.getElementById("left");
    321  const rightNode = document.getElementById("right");
    322 
    323  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    324 
    325  test(() => {
    326    assert_equals(
    327      leftNode.getAttribute("id"),
    328      "left",
    329      `The left node should keep having id=left (isConnected=${leftNode.isConnected}, ${t.name})`
    330    );
    331  });
    332  test(() => {
    333    assert_equals(
    334      rightNode.getAttribute("id"),
    335      "right",
    336      `The right node should keep having id=right (isConnected=${rightNode.isConnected}, ${t.name})`
    337    );
    338  });
    339  assert_equals(
    340    leftNode.isConnected ^ rightNode.isConnected,
    341    1,
    342    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    343  );
    344 }, "Joining <dt id=\"left\"> and <dt id=\"right\">");
    345 
    346 promise_test(async t => {
    347  utils.setupEditingHost(
    348    `<dl><dt class="left">abc${caretForForwardDelete}</dt><dt class="right">${caretForBackSpace}def</dt></dl>`
    349  );
    350  const leftNode = utils.editingHost.querySelector(".left");
    351  const rightNode = utils.editingHost.querySelector(".right");
    352 
    353  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    354 
    355  test(() => {
    356    assert_equals(
    357      leftNode.getAttribute("class"),
    358      "left",
    359      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    360    );
    361  });
    362  test(() => {
    363    assert_equals(
    364      rightNode.getAttribute("class"),
    365      "right",
    366      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    367    );
    368  });
    369  assert_equals(
    370    leftNode.isConnected ^ rightNode.isConnected,
    371    1,
    372    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    373  );
    374 }, "Joining <dt class=\"left\"> and <dt class=\"right\">");
    375 
    376 promise_test(async t => {
    377  utils.setupEditingHost(
    378    `<dl><dt style="font-size:0.8rem">abc${caretForForwardDelete}</dt><dt style="font-weight:bold">${caretForBackSpace}def</dt></dl>`
    379  );
    380  const leftNode = utils.editingHost.querySelector("dt[style]");
    381  const rightNode = leftNode.nextSibling;
    382 
    383  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    384 
    385  // style attribute values shouldn't be touched in this but case, but it's
    386  // okay if the values are not merged.
    387  test(() => {
    388    assert_true(
    389      leftNode.getAttribute("style").includes("font-size"),
    390      `The left node should keep having style attribute containing font-size (style="${leftNode.getAttribute("style")}", ${t.name})`
    391    );
    392    assert_false(
    393      leftNode.getAttribute("style").includes("font-weight"),
    394      `The left node should have font-weight in its style attribute (style="${leftNode.getAttribute("style")}", ${t.name})`
    395    );
    396  });
    397  test(() => {
    398    assert_true(
    399      rightNode.getAttribute("style").includes("font-weight"),
    400      `The right node should keep having style attribute containing font-size (style="${rightNode.getAttribute("style")}", ${t.name})`
    401    );
    402    assert_false(
    403      rightNode.getAttribute("style").includes("font-style"),
    404      `The right node should have font-size in its style attribute (style="${rightNode.getAttribute("style")}", ${t.name})`
    405    );
    406  });
    407  assert_equals(
    408    leftNode.isConnected ^ rightNode.isConnected,
    409    1,
    410    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    411  );
    412 }, "Joining <dt style=\"font-size:0.8rem\"> and <dt style=\"font-weight:bold\">");
    413 
    414 promise_test(async t => {
    415  utils.setupEditingHost(
    416    `<dl><dt data-foo="left">abc${caretForForwardDelete}</dt><dt data-bar="right">${caretForBackSpace}def</dt></dl>`
    417  );
    418  const leftNode = utils.editingHost.querySelector("[data-foo=left]");
    419  const rightNode = utils.editingHost.querySelector("[data-bar=right]");
    420 
    421  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    422 
    423  test(() => {
    424    assert_equals(
    425      leftNode.getAttribute("data-foo"),
    426      "left",
    427      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    428    );
    429  });
    430  test(() => {
    431    assert_false(
    432      leftNode.hasAttribute("data-bar"),
    433      `The left node shouldn't have data-bar attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    434    );
    435  });
    436  test(() => {
    437    assert_equals(
    438      rightNode.getAttribute("data-bar"),
    439      "right",
    440      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    441    );
    442  });
    443  test(() => {
    444    assert_false(
    445      rightNode.hasAttribute("data-foo"),
    446      `The right node shouldn't have data-foo attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    447    );
    448  });
    449  assert_equals(
    450    leftNode.isConnected ^ rightNode.isConnected,
    451    1,
    452    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    453  );
    454 }, "Joining <dt data-foo=\"left\"> and <dt data-bar=\"right\">");
    455 
    456 // Same tests for <dd> elements because they may be handled in a different
    457 // path.
    458 promise_test(async t => {
    459  utils.setupEditingHost(
    460    `<dl><dd id="left">abc${caretForForwardDelete}</dd><dd id="right">${caretForBackSpace}def</dd></dl>`
    461  );
    462  const leftNode = document.getElementById("left");
    463  const rightNode = document.getElementById("right");
    464 
    465  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    466 
    467  test(() => {
    468    assert_equals(
    469      leftNode.getAttribute("id"),
    470      "left",
    471      `The left node should keep having id=left (isConnected=${leftNode.isConnected}, ${t.name})`
    472    );
    473  });
    474  test(() => {
    475    assert_equals(
    476      rightNode.getAttribute("id"),
    477      "right",
    478      `The right node should keep having id=right (isConnected=${rightNode.isConnected}, ${t.name})`
    479    );
    480  });
    481  assert_equals(
    482    leftNode.isConnected ^ rightNode.isConnected,
    483    1,
    484    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    485  );
    486 }, "Joining <dd id=\"left\"> and <dd id=\"right\">");
    487 
    488 promise_test(async t => {
    489  utils.setupEditingHost(
    490    `<dl><dd class="left">abc${caretForForwardDelete}</dd><dd class="right">${caretForBackSpace}def</dd></dl>`
    491  );
    492  const leftNode = utils.editingHost.querySelector(".left");
    493  const rightNode = utils.editingHost.querySelector(".right");
    494 
    495  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    496 
    497  test(() => {
    498    assert_equals(
    499      leftNode.getAttribute("class"),
    500      "left",
    501      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    502    );
    503  });
    504  test(() => {
    505    assert_equals(
    506      rightNode.getAttribute("class"),
    507      "right",
    508      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    509    );
    510  });
    511  assert_equals(
    512    leftNode.isConnected ^ rightNode.isConnected,
    513    1,
    514    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    515  );
    516 }, "Joining <dd class=\"left\"> and <dd class=\"right\">");
    517 
    518 promise_test(async t => {
    519  utils.setupEditingHost(
    520    `<dl><dd style="font-size:0.8rem">abc${caretForForwardDelete}</dd><dd style="font-weight:bold">${caretForBackSpace}def</dd></dl>`
    521  );
    522  const leftNode = utils.editingHost.querySelector("dd[style]");
    523  const rightNode = leftNode.nextSibling;
    524 
    525  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    526 
    527  // style attribute values shouldn't be touched in this but case, but it's
    528  // okay if the values are not merged.
    529  test(() => {
    530    assert_true(
    531      leftNode.getAttribute("style").includes("font-size"),
    532      `The left node should keep having style attribute containing font-size (style="${leftNode.getAttribute("style")}", ${t.name})`
    533    );
    534    assert_false(
    535      leftNode.getAttribute("style").includes("font-weight"),
    536      `The left node should have font-weight in its style attribute (style="${leftNode.getAttribute("style")}", ${t.name})`
    537    );
    538  });
    539  test(() => {
    540    assert_true(
    541      rightNode.getAttribute("style").includes("font-weight"),
    542      `The right node should keep having style attribute containing font-size (style="${rightNode.getAttribute("style")}", ${t.name})`
    543    );
    544    assert_false(
    545      rightNode.getAttribute("style").includes("font-style"),
    546      `The right node should have font-size in its style attribute (style="${rightNode.getAttribute("style")}", ${t.name})`
    547    );
    548  });
    549  assert_equals(
    550    leftNode.isConnected ^ rightNode.isConnected,
    551    1,
    552    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    553  );
    554 }, "Joining <dd style=\"font-size:0.8rem\"> and <dd style=\"font-weight:bold\">");
    555 
    556 promise_test(async t => {
    557  utils.setupEditingHost(
    558    `<dl><dd data-foo="left">abc${caretForForwardDelete}</dd><dd data-bar="right">${caretForBackSpace}def</dd></dl>`
    559  );
    560  const leftNode = utils.editingHost.querySelector("[data-foo=left]");
    561  const rightNode = utils.editingHost.querySelector("[data-bar=right]");
    562 
    563  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    564 
    565  test(() => {
    566    assert_equals(
    567      leftNode.getAttribute("data-foo"),
    568      "left",
    569      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    570    );
    571  });
    572  test(() => {
    573    assert_false(
    574      leftNode.hasAttribute("data-bar"),
    575      `The left node shouldn't have data-bar attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    576    );
    577  });
    578  test(() => {
    579    assert_equals(
    580      rightNode.getAttribute("data-bar"),
    581      "right",
    582      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    583    );
    584  });
    585  test(() => {
    586    assert_false(
    587      rightNode.hasAttribute("data-foo"),
    588      `The right node shouldn't have data-foo attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    589    );
    590  });
    591  assert_equals(
    592    leftNode.isConnected ^ rightNode.isConnected,
    593    1,
    594    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    595  );
    596 }, "Joining <dd data-foo=\"left\"> and <dd data-bar=\"right\">");
    597 
    598 // Same tests for <dt> and <dd> because they may be handled in a different
    599 // path.
    600 promise_test(async t => {
    601  utils.setupEditingHost(
    602    `<dl><dt id="left">abc${caretForForwardDelete}</dt><dd id="right">${caretForBackSpace}def</dd></dl>`
    603  );
    604  const leftNode = document.getElementById("left");
    605  const rightNode = document.getElementById("right");
    606 
    607  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    608 
    609  test(() => {
    610    assert_equals(
    611      leftNode.getAttribute("id"),
    612      "left",
    613      `The left node should keep having id=left (isConnected=${leftNode.isConnected}, ${t.name})`
    614    );
    615  });
    616  test(() => {
    617    assert_equals(
    618      rightNode.getAttribute("id"),
    619      "right",
    620      `The right node should keep having id=right (isConnected=${rightNode.isConnected}, ${t.name})`
    621    );
    622  });
    623  assert_equals(
    624    leftNode.isConnected ^ rightNode.isConnected,
    625    1,
    626    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    627  );
    628 }, "Joining <dt id=\"left\"> and <dd id=\"right\">");
    629 
    630 promise_test(async t => {
    631  utils.setupEditingHost(
    632    `<dl><dt class="left">abc${caretForForwardDelete}</dt><dd class="right">${caretForBackSpace}def</dd></dl>`
    633  );
    634  const leftNode = utils.editingHost.querySelector(".left");
    635  const rightNode = utils.editingHost.querySelector(".right");
    636 
    637  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    638 
    639  test(() => {
    640    assert_equals(
    641      leftNode.getAttribute("class"),
    642      "left",
    643      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    644    );
    645  });
    646  test(() => {
    647    assert_equals(
    648      rightNode.getAttribute("class"),
    649      "right",
    650      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    651    );
    652  });
    653  assert_equals(
    654    leftNode.isConnected ^ rightNode.isConnected,
    655    1,
    656    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    657  );
    658 }, "Joining <dt class=\"left\"> and <dd class=\"right\">");
    659 
    660 promise_test(async t => {
    661  utils.setupEditingHost(
    662    `<dl><dt style="font-size:0.8rem">abc${caretForForwardDelete}</dt><dd style="font-weight:bold">${caretForBackSpace}def</dd></dl>`
    663  );
    664  const leftNode = utils.editingHost.querySelector("dt[style]");
    665  const rightNode = utils.editingHost.querySelector("dd[style]");
    666 
    667  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    668 
    669  // style attribute values shouldn't be touched in this but case, but it's
    670  // okay if the values are not merged.
    671  test(() => {
    672    assert_true(
    673      leftNode.getAttribute("style").includes("font-size"),
    674      `The left node should keep having style attribute containing font-size (style="${leftNode.getAttribute("style")}", ${t.name})`
    675    );
    676    assert_false(
    677      leftNode.getAttribute("style").includes("font-weight"),
    678      `The left node should have font-weight in its style attribute (style="${leftNode.getAttribute("style")}", ${t.name})`
    679    );
    680  });
    681  test(() => {
    682    assert_true(
    683      rightNode.getAttribute("style").includes("font-weight"),
    684      `The right node should keep having style attribute containing font-size (style="${rightNode.getAttribute("style")}", ${t.name})`
    685    );
    686    assert_false(
    687      rightNode.getAttribute("style").includes("font-style"),
    688      `The right node should have font-size in its style attribute (style="${rightNode.getAttribute("style")}", ${t.name})`
    689    );
    690  });
    691  assert_equals(
    692    leftNode.isConnected ^ rightNode.isConnected,
    693    1,
    694    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    695  );
    696 }, "Joining <dt style=\"font-size:0.8rem\"> and <dd style=\"font-weight:bold\">");
    697 
    698 promise_test(async t => {
    699  utils.setupEditingHost(
    700    `<dl><dt data-foo="left">abc${caretForForwardDelete}</dt><dd data-bar="right">${caretForBackSpace}def</dd></dl>`
    701  );
    702  const leftNode = utils.editingHost.querySelector("[data-foo=left]");
    703  const rightNode = utils.editingHost.querySelector("[data-bar=right]");
    704 
    705  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    706 
    707  test(() => {
    708    assert_equals(
    709      leftNode.getAttribute("data-foo"),
    710      "left",
    711      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    712    );
    713  });
    714  test(() => {
    715    assert_false(
    716      leftNode.hasAttribute("data-bar"),
    717      `The left node shouldn't have data-bar attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    718    );
    719  });
    720  test(() => {
    721    assert_equals(
    722      rightNode.getAttribute("data-bar"),
    723      "right",
    724      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    725    );
    726  });
    727  test(() => {
    728    assert_false(
    729      rightNode.hasAttribute("data-foo"),
    730      `The right node shouldn't have data-foo attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    731    );
    732  });
    733  assert_equals(
    734    leftNode.isConnected ^ rightNode.isConnected,
    735    1,
    736    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    737  );
    738 }, "Joining <dt data-foo=\"left\"> and <dd data-bar=\"right\">");
    739 
    740 // Same tests for <dd> and <dt> because they may be handled in a different
    741 // path.
    742 promise_test(async t => {
    743  utils.setupEditingHost(
    744    `<dl><dd id="left">abc${caretForForwardDelete}</dd><dt id="right">${caretForBackSpace}def</dt></dl>`
    745  );
    746  const leftNode = document.getElementById("left");
    747  const rightNode = document.getElementById("right");
    748 
    749  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    750 
    751  test(() => {
    752    assert_equals(
    753      leftNode.getAttribute("id"),
    754      "left",
    755      `The left node should keep having id=left (isConnected=${leftNode.isConnected}, ${t.name})`
    756    );
    757  });
    758  test(() => {
    759    assert_equals(
    760      rightNode.getAttribute("id"),
    761      "right",
    762      `The right node should keep having id=right (isConnected=${rightNode.isConnected}, ${t.name})`
    763    );
    764  });
    765  assert_equals(
    766    leftNode.isConnected ^ rightNode.isConnected,
    767    1,
    768    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    769  );
    770 }, "Joining <dd id=\"left\"> and <dt id=\"right\">");
    771 
    772 promise_test(async t => {
    773  utils.setupEditingHost(
    774    `<dl><dd class="left">abc${caretForForwardDelete}</dd><dt class="right">${caretForBackSpace}def</dt></dl>`
    775  );
    776  const leftNode = utils.editingHost.querySelector(".left");
    777  const rightNode = utils.editingHost.querySelector(".right");
    778 
    779  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    780 
    781  test(() => {
    782    assert_equals(
    783      leftNode.getAttribute("class"),
    784      "left",
    785      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    786    );
    787  });
    788  test(() => {
    789    assert_equals(
    790      rightNode.getAttribute("class"),
    791      "right",
    792      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    793    );
    794  });
    795  assert_equals(
    796    leftNode.isConnected ^ rightNode.isConnected,
    797    1,
    798    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    799  );
    800 }, "Joining <dd class=\"left\"> and <dt class=\"right\">");
    801 
    802 promise_test(async t => {
    803  utils.setupEditingHost(
    804    `<dl><dd style="font-size:0.8rem">abc${caretForForwardDelete}</dd><dt style="font-weight:bold">${caretForBackSpace}def</dt></dl>`
    805  );
    806  const leftNode = utils.editingHost.querySelector("dd[style]");
    807  const rightNode = utils.editingHost.querySelector("dt[style]");
    808 
    809  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    810 
    811  // style attribute values shouldn't be touched in this but case, but it's
    812  // okay if the values are not merged.
    813  test(() => {
    814    assert_true(
    815      leftNode.getAttribute("style").includes("font-size"),
    816      `The left node should keep having style attribute containing font-size (style="${leftNode.getAttribute("style")}", ${t.name})`
    817    );
    818    assert_false(
    819      leftNode.getAttribute("style").includes("font-weight"),
    820      `The left node should have font-weight in its style attribute (style="${leftNode.getAttribute("style")}", ${t.name})`
    821    );
    822  });
    823  test(() => {
    824    assert_true(
    825      rightNode.getAttribute("style").includes("font-weight"),
    826      `The right node should keep having style attribute containing font-size (style="${rightNode.getAttribute("style")}", ${t.name})`
    827    );
    828    assert_false(
    829      rightNode.getAttribute("style").includes("font-style"),
    830      `The right node should have font-size in its style attribute (style="${rightNode.getAttribute("style")}", ${t.name})`
    831    );
    832  });
    833  assert_equals(
    834    leftNode.isConnected ^ rightNode.isConnected,
    835    1,
    836    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    837  );
    838 }, "Joining <dd style=\"font-size:0.8rem\"> and <dt style=\"font-weight:bold\">");
    839 
    840 promise_test(async t => {
    841  utils.setupEditingHost(
    842    `<dl><dd data-foo="left">abc${caretForForwardDelete}</dd><dt data-bar="right">${caretForBackSpace}def</dt></dl>`
    843  );
    844  const leftNode = utils.editingHost.querySelector("[data-foo=left]");
    845  const rightNode = utils.editingHost.querySelector("[data-bar=right]");
    846 
    847  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    848 
    849  test(() => {
    850    assert_equals(
    851      leftNode.getAttribute("data-foo"),
    852      "left",
    853      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    854    );
    855  });
    856  test(() => {
    857    assert_false(
    858      leftNode.hasAttribute("data-bar"),
    859      `The left node shouldn't have data-bar attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    860    );
    861  });
    862  test(() => {
    863    assert_equals(
    864      rightNode.getAttribute("data-bar"),
    865      "right",
    866      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    867    );
    868  });
    869  test(() => {
    870    assert_false(
    871      rightNode.hasAttribute("data-foo"),
    872      `The right node shouldn't have data-foo attribute (isConnected=${leftNode.isConnected}, ${t.name})`
    873    );
    874  });
    875  assert_equals(
    876    leftNode.isConnected ^ rightNode.isConnected,
    877    1,
    878    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    879  );
    880 }, "Joining <dd data-foo=\"left\"> and <dt data-bar=\"right\">");
    881 
    882 // Same tests for <h3> and <div> because they may be handled in a different
    883 // path.
    884 promise_test(async t => {
    885  utils.setupEditingHost(
    886    `<h3 id="left">abc${caretForForwardDelete}</h3><div id="right">${caretForBackSpace}def</div>`
    887  );
    888  const leftNode = document.getElementById("left");
    889  const rightNode = document.getElementById("right");
    890 
    891  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    892 
    893  test(() => {
    894    assert_equals(
    895      leftNode.getAttribute("id"),
    896      "left",
    897      `The left node should keep having id=left (isConnected=${leftNode.isConnected}, ${t.name})`
    898    );
    899  });
    900  test(() => {
    901    assert_equals(
    902      rightNode.getAttribute("id"),
    903      "right",
    904      `The right node should keep having id=right (isConnected=${rightNode.isConnected}, ${t.name})`
    905    );
    906  });
    907  assert_equals(
    908    leftNode.isConnected ^ rightNode.isConnected,
    909    1,
    910    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    911  );
    912 }, "Joining <h3 id=\"left\"> and <div id=\"right\">");
    913 
    914 promise_test(async t => {
    915  utils.setupEditingHost(
    916    `<h3 class="left">abc${caretForForwardDelete}</h3><div class="right">${caretForBackSpace}def</div>`
    917  );
    918  const leftNode = utils.editingHost.querySelector(".left");
    919  const rightNode = utils.editingHost.querySelector(".right");
    920 
    921  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    922 
    923  test(() => {
    924    assert_equals(
    925      leftNode.getAttribute("class"),
    926      "left",
    927      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    928    );
    929  });
    930  test(() => {
    931    assert_equals(
    932      rightNode.getAttribute("class"),
    933      "right",
    934      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
    935    );
    936  });
    937  assert_equals(
    938    leftNode.isConnected ^ rightNode.isConnected,
    939    1,
    940    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    941  );
    942 }, "Joining <h3 class=\"left\"> and <div class=\"right\">");
    943 
    944 promise_test(async t => {
    945  utils.setupEditingHost(
    946    `<h3 style="font-size:0.8rem">abc${caretForForwardDelete}</h3><div style="font-weight:bold">${caretForBackSpace}def</div>`
    947  );
    948  const leftNode = utils.editingHost.querySelector("h3[style]");
    949  const rightNode = utils.editingHost.querySelector("div[style]");
    950 
    951  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    952 
    953  // style attribute values shouldn't be touched in this but case, but it's
    954  // okay if the values are not merged.
    955  test(() => {
    956    assert_true(
    957      leftNode.getAttribute("style").includes("font-size"),
    958      `The left node should keep having style attribute containing font-size (style="${leftNode.getAttribute("style")}", ${t.name})`
    959    );
    960    assert_false(
    961      leftNode.getAttribute("style").includes("font-weight"),
    962      `The left node should have font-weight in its style attribute (style="${leftNode.getAttribute("style")}", ${t.name})`
    963    );
    964  });
    965  test(() => {
    966    assert_true(
    967      rightNode.getAttribute("style").includes("font-weight"),
    968      `The right node should keep having style attribute containing font-size (style="${rightNode.getAttribute("style")}", ${t.name})`
    969    );
    970    assert_false(
    971      rightNode.getAttribute("style").includes("font-style"),
    972      `The right node should have font-size in its style attribute (style="${rightNode.getAttribute("style")}", ${t.name})`
    973    );
    974  });
    975  assert_equals(
    976    leftNode.isConnected ^ rightNode.isConnected,
    977    1,
    978    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
    979  );
    980 }, "Joining <h3 style=\"font-size:0.8rem\"> and <div style=\"font-weight:bold\">");
    981 
    982 promise_test(async t => {
    983  utils.setupEditingHost(
    984    `<h3 data-foo="left">abc${caretForForwardDelete}</h3><div data-bar="right">${caretForBackSpace}def</div>`
    985  );
    986  const leftNode = utils.editingHost.querySelector("[data-foo=left]");
    987  const rightNode = utils.editingHost.querySelector("[data-bar=right]");
    988 
    989  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
    990 
    991  test(() => {
    992    assert_equals(
    993      leftNode.getAttribute("data-foo"),
    994      "left",
    995      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
    996    );
    997  });
    998  test(() => {
    999    assert_false(
   1000      leftNode.hasAttribute("data-bar"),
   1001      `The left node shouldn't have data-bar attribute (isConnected=${leftNode.isConnected}, ${t.name})`
   1002    );
   1003  });
   1004  test(() => {
   1005    assert_equals(
   1006      rightNode.getAttribute("data-bar"),
   1007      "right",
   1008      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
   1009    );
   1010  });
   1011  test(() => {
   1012    assert_false(
   1013      rightNode.hasAttribute("data-foo"),
   1014      `The right node shouldn't have data-foo attribute (isConnected=${leftNode.isConnected}, ${t.name})`
   1015    );
   1016  });
   1017  assert_equals(
   1018    leftNode.isConnected ^ rightNode.isConnected,
   1019    1,
   1020    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
   1021  );
   1022 }, "Joining <h3 data-foo=\"left\"> and <div data-bar=\"right\">");
   1023 
   1024 // Same tests for <div> and <h3> because they may be handled in a different
   1025 // path.
   1026 promise_test(async t => {
   1027  utils.setupEditingHost(
   1028    `<div id="left">abc${caretForForwardDelete}</div><h3 id="right">${caretForBackSpace}def</h3>`
   1029  );
   1030  const leftNode = document.getElementById("left");
   1031  const rightNode = document.getElementById("right");
   1032 
   1033  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
   1034 
   1035  test(() => {
   1036    assert_equals(
   1037      leftNode.getAttribute("id"),
   1038      "left",
   1039      `The left node should keep having id=left (isConnected=${leftNode.isConnected}, ${t.name})`
   1040    );
   1041  });
   1042  test(() => {
   1043    assert_equals(
   1044      rightNode.getAttribute("id"),
   1045      "right",
   1046      `The right node should keep having id=right (isConnected=${rightNode.isConnected}, ${t.name})`
   1047    );
   1048  });
   1049  assert_equals(
   1050    leftNode.isConnected ^ rightNode.isConnected,
   1051    1,
   1052    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
   1053  );
   1054 }, "Joining <div id=\"left\"> and <h3 id=\"right\">");
   1055 
   1056 promise_test(async t => {
   1057  utils.setupEditingHost(
   1058    `<div class="left">abc${caretForForwardDelete}</div><h3 class="right">${caretForBackSpace}def</h3>`
   1059  );
   1060  const leftNode = utils.editingHost.querySelector(".left");
   1061  const rightNode = utils.editingHost.querySelector(".right");
   1062 
   1063  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
   1064 
   1065  test(() => {
   1066    assert_equals(
   1067      leftNode.getAttribute("class"),
   1068      "left",
   1069      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
   1070    );
   1071  });
   1072  test(() => {
   1073    assert_equals(
   1074      rightNode.getAttribute("class"),
   1075      "right",
   1076      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
   1077    );
   1078  });
   1079  assert_equals(
   1080    leftNode.isConnected ^ rightNode.isConnected,
   1081    1,
   1082    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
   1083  );
   1084 }, "Joining <div class=\"left\"> and <h3 class=\"right\">");
   1085 
   1086 promise_test(async t => {
   1087  utils.setupEditingHost(
   1088    `<div style="font-size:0.8rem">abc${caretForForwardDelete}</div><h3 style="font-weight:bold">${caretForBackSpace}def</h3>`
   1089  );
   1090  const leftNode = utils.editingHost.querySelector("div[style]");
   1091  const rightNode = utils.editingHost.querySelector("h3[style]");
   1092 
   1093  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
   1094 
   1095  // style attribute values shouldn't be touched in this but case, but it's
   1096  // okay if the values are not merged.
   1097  test(() => {
   1098    assert_true(
   1099      leftNode.getAttribute("style").includes("font-size"),
   1100      `The left node should keep having style attribute containing font-size (style="${leftNode.getAttribute("style")}", ${t.name})`
   1101    );
   1102    assert_false(
   1103      leftNode.getAttribute("style").includes("font-weight"),
   1104      `The left node should have font-weight in its style attribute (style="${leftNode.getAttribute("style")}", ${t.name})`
   1105    );
   1106  });
   1107  test(() => {
   1108    assert_true(
   1109      rightNode.getAttribute("style").includes("font-weight"),
   1110      `The right node should keep having style attribute containing font-size (style="${rightNode.getAttribute("style")}", ${t.name})`
   1111    );
   1112    assert_false(
   1113      rightNode.getAttribute("style").includes("font-style"),
   1114      `The right node should have font-size in its style attribute (style="${rightNode.getAttribute("style")}", ${t.name})`
   1115    );
   1116  });
   1117  assert_equals(
   1118    leftNode.isConnected ^ rightNode.isConnected,
   1119    1,
   1120    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
   1121  );
   1122 }, "Joining <div style=\"font-size:0.8rem\"> and <h3 style=\"font-weight:bold\">");
   1123 
   1124 promise_test(async t => {
   1125  utils.setupEditingHost(
   1126    `<div data-foo="left">abc${caretForForwardDelete}</div><h3 data-bar="right">${caretForBackSpace}def</h3>`
   1127  );
   1128  const leftNode = utils.editingHost.querySelector("[data-foo=left]");
   1129  const rightNode = utils.editingHost.querySelector("[data-bar=right]");
   1130 
   1131  await (testingBackspace ? utils.sendBackspaceKey() : utils.sendDeleteKey());
   1132 
   1133  test(() => {
   1134    assert_equals(
   1135      leftNode.getAttribute("data-foo"),
   1136      "left",
   1137      `The left node should keep having class=left (isConnected=${leftNode.isConnected}, ${t.name})`
   1138    );
   1139  });
   1140  test(() => {
   1141    assert_false(
   1142      leftNode.hasAttribute("data-bar"),
   1143      `The left node shouldn't have data-bar attribute (isConnected=${leftNode.isConnected}, ${t.name})`
   1144    );
   1145  });
   1146  test(() => {
   1147    assert_equals(
   1148      rightNode.getAttribute("data-bar"),
   1149      "right",
   1150      `The right node should keep having class=right (isConnected=${rightNode.isConnected}, ${t.name})`
   1151    );
   1152  });
   1153  test(() => {
   1154    assert_false(
   1155      rightNode.hasAttribute("data-foo"),
   1156      `The right node shouldn't have data-foo attribute (isConnected=${leftNode.isConnected}, ${t.name})`
   1157    );
   1158  });
   1159  assert_equals(
   1160    leftNode.isConnected ^ rightNode.isConnected,
   1161    1,
   1162    `One should stay in the document and the other should be disconnected (${utils.editingHost.innerHTML}, ${t.name})`
   1163  );
   1164 }, "Joining <div data-foo=\"left\"> and <h3 data-bar=\"right\">");
   1165 
   1166 
   1167 </script>