chromium/third_party/blink/web_tests/external/wpt/input-events/input-events-get-target-ranges-deleting-in-list-items.tentative.html

<!DOCTYPE html>
<meta charset="utf-8">
<meta name="timeout" content="long">
<meta name="variant" content="?Backspace,ul">
<meta name="variant" content="?Backspace,ol">
<meta name="variant" content="?Delete,ul">
<meta name="variant" content="?Delete,ol">
<title>InputEvent.getTargetRanges() at deleting in/around/across list item elements</title>
<div contenteditable></div>
<script src="input-events-get-target-ranges.js"></script>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script>
"use strict";

const [action, list] = location.search.substring(1).split(",");
function run() {
  switch (action) {
    case "Backspace":
      return sendBackspaceKey();
    case "Delete":
      return sendDeleteKey();
    default:
      throw "Unhandled variant";
  }
}

/**
 * @param innerHTML     Initial `innerHTML` value of the editor.
 * @param data
 *          expectedInnerHTML
 *                      Expected `innerHTML` of the editor after calling
 *                      `run()`.  This can be array of string if there are
 *                      some acceptable differences like whether there is
 *                      an invisible `<br>` element at end of list item.
 *          expectedTargetRanges
 *                      `null` or `unspecified` if `beforeinput` event shouldn't
 *                      be fired.
 *                      Otherwise, function returning an array of objects
 *                      which have `startContainer`, `startOffset`,
 *                      `endContainer`, `endOffset`.  This will be called
 *                      before calling `run()` and compared with
 *                      `getTargetRanges()` after that.
 *          expectInputEvent:
 *                      `true` if it should cause an `input` event.
 */
function addPromiseTest(innerHTML, data) {
  promise_test(async (t) => {
    initializeTest(innerHTML);
    let expectedTargetRanges =
      typeof data.expectedTargetRanges === "function"
        ? data.expectedTargetRanges()
        : null;
    await run();
    checkEditorContentResultAsSubTest(data.expectedInnerHTML, t.name);
    if (expectedTargetRanges !== null) {
      checkGetTargetRangesOfBeforeinputOnDeleteSomething(expectedTargetRanges);
      if (data.expectInputEvent) {
        checkGetTargetRangesOfInputOnDeleteSomething();
      } else {
        checkGetTargetRangesOfInputOnDoNothing();
      }
    } else {
      checkBeforeinputAndInputEventsOnNOOP();
    }
  }, `${action} at "${innerHTML}"`);
}

addPromiseTest(
  `<${list}><li>list[-item1</li><li>list]-item2</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item2</li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: "list".length,
          endContainer: gEditor.querySelector("li + li").firstChild,
          endOffset: "list".length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>list-[item1</li><li>]list-item2</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-list-item2</li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: "list-".length,
          endContainer: gEditor.querySelector("li + li").firstChild,
          endOffset: 0,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>list-[item1</li><li>}list-item2</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-list-item2</li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: "list-".length,
          endContainer: gEditor.querySelector("li + li"),
          endOffset: 0,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>list-item1[</li><li>list]-item2</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1-item2</li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: gEditor.querySelector("li").firstChild.length,
          endContainer: gEditor.querySelector("li + li").firstChild,
          endOffset: "list".length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>list-item1{</li><li>list]-item2</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1-item2</li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li"),
          startOffset: 1,
          endContainer: gEditor.querySelector("li + li").firstChild,
          endOffset: "list".length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>list-item1[</li><li>]list-item2</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1list-item2</li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: gEditor.querySelector("li").firstChild.length,
          endContainer: gEditor.querySelector("li + li").firstChild,
          endOffset: 0,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  action === "Backspace"
    ? `<${list}><li>list-item1</li><li>[]list-item2</li></${list}>`
    : `<${list}><li>list-item1[]</li><li>list-item2</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1list-item2</li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: gEditor.querySelector("li").firstChild.length,
          endContainer: gEditor.querySelector("li + li").firstChild,
          endOffset: 0,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  action === "Backspace"
    ? `<${list}><li>list-item1<br></li><li>[]list-item2</li></${list}>`
    : `<${list}><li>list-item1[]<br></li><li>list-item2</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1list-item2</li></${list}>`,
    expectedTargetRanges: () => {
      return action === "Backspace"
        ? [
            {
              startContainer: gEditor.querySelector("li"),
              startOffset: 1,
              endContainer: gEditor.querySelector("li + li").firstChild,
              endOffset: 0,
            },
          ]
        : [
            {
              startContainer: gEditor.querySelector("li").firstChild,
              startOffset: gEditor.querySelector("li").firstChild.length,
              endContainer: gEditor.querySelector("li + li").firstChild,
              endOffset: 0,
            },
          ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  action === "Backspace"
    ? `<${list}><li>list-item1<br><br></li><li>[]list-item2</li></${list}>`
    : `<${list}><li>list-item1[]<br><br></li><li>list-item2</li></${list}>`,
  {
    expectedInnerHTML: [
      `<${list}><li>list-item1<br>list-item2</li></${list}>`,
      `<${list}><li>list-item1<br>list-item2<br></li></${list}>`,
    ],
    expectedTargetRanges: () => {
      return action === "Backspace"
        ? [
            {
              startContainer: gEditor.querySelector("li"),
              startOffset: 1,
              endContainer: gEditor.querySelector("li + li").firstChild,
              endOffset: 0,
            },
          ]
        : [
            {
              startContainer: gEditor.querySelector("li").firstChild,
              startOffset: gEditor.querySelector("li").firstChild.length,
              endContainer: gEditor.querySelector("li + li").firstChild,
              endOffset: 0,
            },
          ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  action === "Backspace"
    ? `<${list}><li>list-item1</li><li>[]list-item2<br>second line of list-item2</li></${list}>`
    : `<${list}><li>list-item1[]</li><li>list-item2<br>second line of list-item2</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1list-item2</li><li>second line of list-item2</li></${list}>`,
    expectedTargetRanges: () => {
      return action === "Backspace"
        ? [
            {
              startContainer: gEditor.querySelector("li"),
              startOffset: 1,
              endContainer: gEditor.querySelector("li + li").firstChild,
              endOffset: 0,
            },
          ]
        : [
            {
              startContainer: gEditor.querySelector("li").firstChild,
              startOffset: gEditor.querySelector("li").firstChild.length,
              endContainer: gEditor.querySelector("li + li").firstChild,
              endOffset: 0,
            },
          ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  action === "Backspace"
    ? `<${list}><li><p>list-item1</p></li><li>[]list-item2</li></${list}>`
    : `<${list}><li><p>list-item1[]</p></li><li>list-item2</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li><p>list-item1list-item2</p></li></${list}>`,
    expectedTargetRanges: () => {
      return action === "Backspace"
        ? [
            {
              startContainer: gEditor.querySelector("p").firstChild,
              startOffset: gEditor.querySelector("p").firstChild.length,
              endContainer: gEditor.querySelector("li + li").firstChild,
              endOffset: 0,
            },
          ]
        : [
            {
              startContainer: gEditor.querySelector("p").firstChild,
              startOffset: gEditor.querySelector("p").firstChild.length,
              endContainer: gEditor.querySelector("li + li").firstChild,
              endOffset: 0,
            },
          ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  action === "Backspace"
    ? `<${list}><li>list-item1</li><li><p>[]list-item2</p></li></${list}>`
    : `<${list}><li>list-item1[]</li><li><p>list-item2</p></li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1list-item2</li></${list}>`,
    expectedTargetRanges: () => {
      return action === "Backspace"
        ? [
            {
              startContainer: gEditor.querySelector("li").firstChild,
              startOffset: gEditor.querySelector("li").firstChild.length,
              endContainer: gEditor.querySelector("p").firstChild,
              endOffset: 0,
            },
          ]
        : [
            {
              startContainer: gEditor.querySelector("li").firstChild,
              startOffset: gEditor.querySelector("li").firstChild.length,
              endContainer: gEditor.querySelector("p").firstChild,
              endOffset: 0,
            },
          ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>[list-item1]</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li><br></li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector("li").firstChild,
          endOffset: gEditor.querySelector("li").firstChild.length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>{list-item1}</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li><br></li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector("li").firstChild,
          endOffset: gEditor.querySelector("li").firstChild.length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

// Even if the last list item is selected, don't delete the list and
// the last list item element.  This is a triple click case on Gecko.
addPromiseTest(
  `<${list}>{<li>list-item1</li>}</${list}>`,
  {
    expectedInnerHTML: `<${list}><li><br></li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector("li").firstChild,
          endOffset: gEditor.querySelector("li").firstChild.length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

// A list item is selected and it's not the last one, can delete it.
addPromiseTest(
  `<${list}>{<li>list-item1</li>}<li>list-item2</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item2</li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector(`${list}`),
          startOffset: 0,
          endContainer: gEditor.querySelector(`${list}`),
          endOffset: 1,
        },
      ];
    },
    expectInputEvent: true,
  }
);

// Delete list element when deleting from empty last list item.
addPromiseTest(
  `<${list}><li>{}<br></li></${list}>`,
  {
    expectedInnerHTML: ["", "<br>", "<div><br></div>"],
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor,
          startOffset: 0,
          endContainer: gEditor,
          endOffset: 1,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `{<${list}><li><br></li></${list}>}`,
  {
    expectedInnerHTML: ["", "<br>", "<div><br></div>"],
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor,
          startOffset: 0,
          endContainer: gEditor,
          endOffset: 1,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<div>{<${list}><li><br></li></${list}>}</div>`,
  {
    expectedInnerHTML: ["<div><br></div>", "<div><div><br></div></div>"],
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("div"),
          startOffset: 0,
          endContainer: gEditor.querySelector("div"),
          endOffset: 1,
        },
      ];
    },
    expectInputEvent: true,
  }
);

// It may be better to ignore the invisible white-space and take same action
// as above, but it requires more expensive check before deleting.  So perhaps,
// this behavior is reasonable.
addPromiseTest(
  `<div>{  <${list}><li><br></li></${list}>  }</div>`,
  {
    expectedInnerHTML: ["<div><br></div>", "<div><div><br></div></div>"],
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("div"),
          startOffset: 0,
          endContainer: gEditor.querySelector("div"),
          endOffset: 3,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<div><${list}><li>{}<br></li></${list}></div>`,
  {
    expectedInnerHTML: ["<div><br></div>", "<div><div><br></div></div>"],
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("div"),
          startOffset: 0,
          endContainer: gEditor.querySelector("div"),
          endOffset: 1,
        },
      ];
    },
    expectInputEvent: true,
  }
);

// XXX Blink does not delete the list element if its first or last <li> element
//     is not editable.  However, it means that user cannot delete the list
//     element, and it's not consistent behavior when only middle list item(s)
//     are not editable.  Perhaps, once it makes the list element has only
//     one empty list item element, then, another deleting operation allows to
//     delete the list element.
addPromiseTest(
  `<div>{<${list}><li contenteditable="false"><br></li></${list}>}</div>`,
  {
    expectedInnerHTML: `<div><${list}><li><br></li></${list}></div>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector(list),
          startOffset: 0,
          endContainer: gEditor.querySelector(list),
          endOffset: 1,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<div>{<${list}><li contenteditable="false">list-item1</li></${list}>}</div>`,
  {
    expectedInnerHTML: `<div><${list}><li><br></li></${list}></div>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector(list),
          startOffset: 0,
          endContainer: gEditor.querySelector(list),
          endOffset: 1,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<div>{<${list}><li contenteditable="false">list-item1</li><li><br></li></${list}>}</div>`,
  {
    expectedInnerHTML: `<div><${list}><li><br></li></${list}></div>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector(list),
          startOffset: 0,
          endContainer: gEditor.querySelector("li + li"),
          endOffset: 1,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<div>{<${list}><li contenteditable="false">list-item1</li><li>list-item2</li></${list}>}</div>`,
  {
    expectedInnerHTML: `<div><${list}><li><br></li></${list}></div>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector(list),
          startOffset: 0,
          endContainer: gEditor.querySelector("li + li").firstChild,
          endOffset: gEditor.querySelector("li + li").firstChild.length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<div>{<${list}><li><br></li><li contenteditable="false">list-item2</li></${list}>}</div>`,
  {
    expectedInnerHTML: `<div><${list}><li><br></li></${list}></div>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector(list),
          endOffset: 2,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<div>{<${list}><li>list-item1</li><li contenteditable="false">list-item2</li></${list}>}</div>`,
  {
    expectedInnerHTML: `<div><${list}><li><br></li></${list}></div>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector(list),
          endOffset: 2,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<div>{<${list}><li><br></li><li contenteditable="false">list-item2</li><li><br></li></${list}>}</div>`,
  {
    expectedInnerHTML: `<div><${list}><li><br></li></${list}></div>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector("li + li + li"),
          endOffset: 1,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<div>{<${list}><li>list-item1</li><li contenteditable="false">list-item2</li><li>list-item3</li></${list}>}</div>`,
  {
    expectedInnerHTML: `<div><${list}><li><br></li></${list}></div>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector("li + li + li").firstChild,
          endOffset: gEditor.querySelector("li + li + li").firstChild.length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>list-item1</li>{<li>list-item2</li>}<li>list-item3</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1</li><li>list-item3</li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector(`${list}`),
          startOffset: 1,
          endContainer: gEditor.querySelector(`${list}`),
          endOffset: 2,
        },
      ];
    },
    expectInputEvent: true,
  }
);

// Selecting last list item element shouldn't delete the list item.
addPromiseTest(
  `<${list}><li>list-item1</li>{<li>list-item2</li>}</${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1</li><li><br></li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector(`${list} > li + li`).firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector(`${list} > li + li`).firstChild,
          endOffset: gEditor.querySelector(`${list} > li + li`).firstChild.length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>list-item1</li><li>list-item2</li>{<li>list-item3</li>}</${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1</li><li>list-item2</li><li><br></li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector(`${list} > li + li + li`).firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector(`${list} > li + li + li`).firstChild,
          endOffset: gEditor.querySelector(`${list} > li + li + li`).firstChild.length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

for (let childList of ["ul", "ol"]) {
  addPromiseTest(
    `<${list}><li>list-item1</li>{<li>list-item2</li>}<li><${childList}><li><br></li></${childList}></li></${list}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item1</li><li><${childList}><li><br></li></${childList}></li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list}`),
            startOffset: 1,
            endContainer: gEditor.querySelector(`${list}`),
            endOffset: 2,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  // Invalid nested list elements cases.  Treat the nested list element as a list item element.
  addPromiseTest(
    `<${list}><li>list-item1</li>{<li>list-item2</li>}<${childList}><li><br></li></${childList}></${list}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item1</li><${childList}><li><br></li></${childList}></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list}`),
            startOffset: 1,
            endContainer: gEditor.querySelector(`${list}`),
            endOffset: 2,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>list-item1</li><li>list-item2</li>{<${childList}><li><br></li></${childList}>}</${list}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item1</li><li>list-item2</li><li><br></li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list}`),
            startOffset: 2,
            endContainer: gEditor.querySelector(`${list}`),
            endOffset: 3,
          },
        ];
      },
      expectInputEvent: true,
    }
  );
}

// Don't delete list and joined list items when only there content are selected.
addPromiseTest(
  `<${list}><li>[list-item1</li><li>list-item2]</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li><br></li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector("li + li").firstChild,
          endOffset: gEditor.querySelector("li + li").firstChild.length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>[list-item1</li><li>list-item2]</li><li>list-item3</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li><br></li><li>list-item3</li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li").firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector("li + li").firstChild,
          endOffset: gEditor.querySelector("li + li").firstChild.length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>list-item1</li><li>[list-item2]</li><li>list-item3</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1</li><li><br></li><li>list-item3</li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li + li").firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector("li + li").firstChild,
          endOffset: gEditor.querySelector("li + li").firstChild.length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

addPromiseTest(
  `<${list}><li>list-item1</li><li>[list-item2</li><li>list-item3]</li></${list}>`,
  {
    expectedInnerHTML: `<${list}><li>list-item1</li><li><br></li></${list}>`,
    expectedTargetRanges: () => {
      return [
        {
          startContainer: gEditor.querySelector("li + li").firstChild,
          startOffset: 0,
          endContainer: gEditor.querySelector("li + li + li").firstChild,
          endOffset: gEditor.querySelector("li + li + li").firstChild.length,
        },
      ];
    },
    expectInputEvent: true,
  }
);

// Ported tests from editing/delete.js and editing/forwarddelete.js
for (let otherList of ["ul", "ol"]) {
  if (action === "Backspace") {
  addPromiseTest(
    `<${otherList}><li>list-item1</li></${otherList}><${list}><li>l[]ist-item2</li></${list}>`,
    {
      expectedInnerHTML: `<${otherList}><li>list-item1</li></${otherList}><${list}><li>ist-item2</li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${otherList} + ${list} > li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${otherList} + ${list} > li`).firstChild,
            endOffset: "l".length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>list-item1[]</li></${list}><${otherList}><li>list-item2</li></${otherList}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item</li></${list}><${otherList}><li>list-item2</li></${otherList}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector("li").firstChild,
            startOffset: "list-item".length,
            endContainer: gEditor.querySelector("li").firstChild,
            endOffset: "list-item1".length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );
  } else {
    addPromiseTest(
      `<${list}><li>list-item[]1</li></${list}><${otherList}><li>list-item2</li></${otherList}>`,
      {
        expectedInnerHTML: `<${list}><li>list-item</li></${list}><${otherList}><li>list-item2</li></${otherList}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector("li").firstChild,
              startOffset: "list-item".length,
              endContainer: gEditor.querySelector("li").firstChild,
              endOffset: "list-item1".length,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    addPromiseTest(
      `<${otherList}><li>list-item1</li></${otherList}><${list}><li>[]list-item2</li></${list}>`,
      {
        expectedInnerHTML: `<${otherList}><li>list-item1</li></${otherList}><${list}><li>ist-item2</li></${list}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${otherList} + ${list} > li`).firstChild,
              startOffset: 0,
              endContainer: gEditor.querySelector(`${otherList} + ${list} > li`).firstChild,
              endOffset: "l".length,
            },
          ];
        },
        expectInputEvent: true,
      }
    );
  }

  addPromiseTest(
    `<${list}><li>list-item1[</li><li>list-item2]</li></${list}><${otherList}><li>list-item3</li></${otherList}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item1</li></${list}><${otherList}><li>ist-item3</li><li>ist-item4</li></${otherList}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector("li").firstChild,
            startOffset: gEditor.querySelector("li").firstChild.length,
            endContainer: gEditor.querySelector("li + li").firstChild,
            endOffset: gEditor.querySelector("li + li").firstChild.length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );
}


// Invalid nested list element cases.  Traditionally, all browser engines
// insert child list element without wrapping it with a list item element.
// So, keeping the behavior in these cases are important for backward
// compatibility.
// https://bugzilla.mozilla.org/show_bug.cgi?id=487524
for (let childList of ["ul", "ol"]) {
  addPromiseTest(
    `<${list}><li>[list-item1</li><${childList}><li>}list-item2</li></ul></${list}>`,
    {
      expectedInnerHTML: [
        `<${list}><${childList}><li>list-item2</li></${childList}></${list}>`,
        `<${list}><${childList}><li>list-item2<br></li></${childList}></${list}>`,
      ],
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector("li").firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list} > ${childList} > li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>[list-item1</li><${childList}><li>list-item2]</li></${childList}></${list}>`,
    {
      expectedInnerHTML: `<${list}><${childList}><li><br></li></${childList}></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector("li").firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list} > ${childList} > li`).firstChild,
            endOffset: gEditor.querySelector(`${list} > ${childList} > li`).firstChild.length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><${childList}><li>[list-item1</li></${childList}><li>}list-item2</li></${list}>`,
    {
      expectedInnerHTML: [
        `<${list}><${childList}><li>list-item2</li></${childList}></${list}>`,
        `<${list}><${childList}><li>list-item2<br></li></${childList}></${list}>`,
      ],
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > ${childList} > li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list} > li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><${childList}><li>[list-item1</li></${childList}><li>list-item2]</li></${list}>`,
    {
      expectedInnerHTML: `<${list}><${childList}><li><br></li></${childList}></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > ${childList} > li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${childList} + li`).firstChild,
            endOffset: gEditor.querySelector(`${childList} + li`).firstChild.length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><${childList}><li>list-item1</li><li>[list-item2</li></${childList}><li>}list-item3</li></${list}>`,
    {
      expectedInnerHTML: `<${list}><${childList}><li>list-item1</li><li>list-item3</li></${childList}></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > ${childList} > li + li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list} > li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>[list-item1</li><${childList}><li>list-item2</li><li>}list-item3</li></${childList}></${list}>`,
    {
      expectedInnerHTML: [
        `<${list}><${childList}><li>list-item3</li></${childList}></${list}>`,
        `<${list}><${childList}><li>list-item3<br></li></${childList}></${list}>`,
      ],
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list} > ${childList} > li + li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>list-item1</li><li>[list-item2</li><${childList}><li>list-item3</li><li>}list-item4</li></${childList}></${list}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item1</li><${childList}><li>list-item4</li></${childList}></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`li + li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list} > ${childList} > li + li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  // Valid sub list element cases.
  addPromiseTest(
    `<${list}><li>[list-item1</li><li><${childList}><li>list-item2]</li></${childList}></li></${list}>`,
    {
      expectedInnerHTML: `<${list}><li><${childList}><li><br></li></${childList}></li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list} > li > ${childList} > li`).firstChild,
            endOffset: gEditor.querySelector(`${list} > li > ${childList} > li`).firstChild.length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li><${childList}><li>[list-item1</li></${childList}><li>}list-item2</li></${list}>`,
    {
      expectedInnerHTML: [
        `<${list}><li><${childList}><li>list-item2</li></${childList}></li></${list}>`,
        `<${list}><li><${childList}><li>list-item2<br></li></${childList}></li></${list}>`,
      ],
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > li > ${childList} > li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list} > li + li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li><${childList}><li>[list-item1</li></${childList}><li>list-item2]</li></${list}>`,
    {
      expectedInnerHTML: `<${list}><li><${childList}><li><br></li></${childList}></li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > li > ${childList} > li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list} > li + li`).firstChild,
            endOffset: gEditor.querySelector(`${list} > li + li`).firstChild.length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );
}

// When deleting the last list item in a sub list, only the list should
// be removed.  This makes users feel like doing outdent.
for (let childList of ["ul", "ol"]) {
  addPromiseTest(
    `<${list}><li><${childList}><li>{}<br></li></${childList}></li></${list}>`,
    {
      expectedInnerHTML: `<${list}><li><br></li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`li`),
            startOffset: 0,
            endContainer: gEditor.querySelector(`li`),
            endOffset: 1,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li><${childList}><li>[list-item1]</li></${childList}></li></${list}>`,
    {
      expectedInnerHTML: `<${list}><li><${childList}><li><br></li></${childList}></li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`li li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`li li`).firstChild,
            endOffset: gEditor.querySelector(`li li`).firstChild.length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>{<${childList}><li>list-item1</li></${childList}>}</li></${list}>`,
    {
      expectedInnerHTML: `<${list}><li><${childList}><li><br></li></${childList}></li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`li li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`li li`).firstChild,
            endOffset: gEditor.querySelector(`li li`).firstChild.length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li><${childList}><li>{}<br></li></${childList}></li><li>list-item2</li></${list}>`,
    {
      expectedInnerHTML: `<${list}><li><br></li><li>list-item2</li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`li`),
            startOffset: 0,
            endContainer: gEditor.querySelector(`li`),
            endOffset: 1,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>list-item1</li><li><${childList}><li>{}<br></li></${childList}></li></${list}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item1</li><li><br></li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`li + li`),
            startOffset: 0,
            endContainer: gEditor.querySelector(`li + li`),
            endOffset: 1,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  // Invalid cases.
  addPromiseTest(
    `<${list}><${childList}><li>{}<br></li></${childList}></${list}>`,
    {
      expectedInnerHTML: `<${list}><li><br></li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list}`),
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list}`),
            endOffset: 1,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><${childList}><li>[list-item1]</li></${childList}></${list}>`,
    {
      expectedInnerHTML: `<${list}><${childList}><li><br></li></${childList}></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`li`).firstChild,
            endOffset: gEditor.querySelector(`li`).firstChild.length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}>{<${childList}><li>list-item1</li></${childList}>}</${list}>`,
    {
      expectedInnerHTML: `<${list}><${childList}><li><br></li></${childList}></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`li`).firstChild,
            endOffset: gEditor.querySelector(`li`).firstChild.length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><${childList}><li>{}<br></li></${childList}><li>list-item2</li></${list}>`,
    {
      expectedInnerHTML: `<${list}><li><br></li><li>list-item2</li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list}`),
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list}`),
            endOffset: 1,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>list-item1</li><${childList}><li>{}<br></li></${childList}></${list}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item1</li><li><br></li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list}`),
            startOffset: 1,
            endContainer: gEditor.querySelector(`${list}`),
            endOffset: 2,
          },
        ];
      },
      expectInputEvent: true,
    }
  );
}

// Joining same level list elements.
for (let otherList of ["ul", "ol"]) {
  addPromiseTest(
    `<${list}><li>[list-item1</li></${list}><${otherList}><li>}list-item2</li></${otherList}>`,
    {
      expectedInnerHTML: [
        `<${list}><li>list-item2</li></${list}>`,
        `<${list}><li>list-item2<br></li></${list}>`,
      ],
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list} + ${otherList} > li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>[list-item1</li></${list}><${otherList}><li>list-item2]</li></${otherList}>`,
    {
      expectedInnerHTML: `<${list}><li><br></li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > li`).firstChild,
            startOffset: 0,
            endContainer: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild,
            endOffset: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild.length,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>list-item1[</li></${list}><${otherList}><li>}list-item2</li></${otherList}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item1list-item2</li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > li`).firstChild,
            startOffset: gEditor.querySelector(`${list} > li`).firstChild.length,
            endContainer: gEditor.querySelector(`${list} + ${otherList} > li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>first line in list-item1<br>list-item1[</li></${list}><${otherList}><li>}list-item2</li></${otherList}>`,
    {
      expectedInnerHTML: `<${list}><li>first line in list-item1<br>list-item1list-item2</li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > li > br`).nextSibling,
            startOffset: gEditor.querySelector(`${list} > li > br`).nextSibling.length,
            endContainer: gEditor.querySelector(`${list} + ${otherList} > li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>list-item1[</li></${list}><${otherList}><li>}list-item2<br>second line in list-item2</li></${otherList}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item1list-item2</li></${list}><${otherList}><li>second line in list-item2</li></${otherList}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > li`).firstChild,
            startOffset: gEditor.querySelector(`${list} > li`).firstChild.length,
            endContainer: gEditor.querySelector(`${list} + ${otherList} > li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>list-item1</li><li>list-item2[</li></${list}><${otherList}><li>}list-item3</li></${otherList}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item1</li><li>list-item2list-item3</li></${list}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > li + li`).firstChild,
            startOffset: gEditor.querySelector(`${list} > li + li`).firstChild.length,
            endContainer: gEditor.querySelector(`${list} + ${otherList} > li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );

  addPromiseTest(
    `<${list}><li>list-item1[</li></${list}><${otherList}><li>}list-item2</li><li>list-item3</li></${otherList}>`,
    {
      expectedInnerHTML: `<${list}><li>list-item1list-item2</li></${list}><${otherList}><li>list-item3</li></${otherList}>`,
      expectedTargetRanges: () => {
        return [
          {
            startContainer: gEditor.querySelector(`${list} > li`).firstChild,
            startOffset: gEditor.querySelector(`${list} > li`).firstChild.length,
            endContainer: gEditor.querySelector(`${list} + ${otherList} > li`),
            endOffset: 0,
          },
        ];
      },
      expectInputEvent: true,
    }
  );
}

// Joining nested left list and right list element.  Move the content in first line from selection end in the right
// list item element into end of the left list item element.
for (let childList of ["ul", "ol"]) {
  for (let otherList of ["ul", "ol"]) {
    addPromiseTest(
      `<${list}><li><${childList}><li>[list-item1</li></${childList}></li></${list}><${otherList}><li>}list-item2</li></${otherList}>`,
      {
        expectedInnerHTML: [
          `<${list}><li><${childList}><li>list-item2</li></${childList}></li></${list}>`,
          `<${list}><li><${childList}><li>list-item2<br></li></${childList}></li></${list}>`,
        ],
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > li > ${childList} > li`).firstChild,
              startOffset: 0,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild,
              endOffset: 0,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    addPromiseTest(
      `<${list}><li><${childList}><li>[list-item1</li></${childList}></li></${list}><${otherList}><li>list-item2]</li></${otherList}>`,
      {
        expectedInnerHTML: `<${list}><li><${childList}><li><br></li></${childList}></li></${list}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > li > ${childList} > li`).firstChild,
              startOffset: 0,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild,
              endOffset: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild.length,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    addPromiseTest(
      `<${list}><li><${childList}><li>list-item1[</li></${childList}></li></${list}><${otherList}><li>list-item2]</li></${otherList}>`,
      {
        expectedInnerHTML: `<${list}><li><${childList}><li>list-item1</li></${childList}></li></${list}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > li > ${childList} > li`).firstChild,
              startOffset: gEditor.querySelector(`${list} > li > ${childList} > li`).firstChild.length,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild,
              endOffset: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild.length,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    // Invalid cases.
    addPromiseTest(
      `<${list}><${childList}><li>[list-item1</li></${childList}></${list}><${otherList}><li>}list-item2</li></${otherList}>`,
      {
        expectedInnerHTML: [
          `<${list}><${childList}><li>list-item2</li></${childList}></${list}>`,
          `<${list}><${childList}><li>list-item2<br></li></${childList}></${list}>`,
        ],
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > ${childList} > li`).firstChild,
              startOffset: 0,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild,
              endOffset: 0,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    addPromiseTest(
      `<${list}><${childList}><li>[list-item1</li></${childList}></${list}><${otherList}><li>list-item2]</li></${otherList}>`,
      {
        expectedInnerHTML: `<${list}><${childList}><li><br></li></${childList}></${list}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > ${childList} > li`).firstChild,
              startOffset: 0,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild,
              endOffset: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild.length,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    addPromiseTest(
      `<${list}><${childList}><li>list-item1[</li></${childList}></${list}><${otherList}><li>list-item2]</li></${otherList}>`,
      {
        expectedInnerHTML: `<${list}><${childList}><li>list-item1</li></${childList}></${list}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > ${childList} > li`).firstChild,
              startOffset: gEditor.querySelector(`${list} > ${childList} > li`).firstChild.length,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild,
              endOffset: gEditor.querySelector(`${list} + ${otherList} > li`).firstChild.length,
            },
          ];
        },
        expectInputEvent: true,
      }
    );
  }
}

// Joining left list and nested right list element.  Basically, the first line from the selection end should
// be moved into the end of the left list item element, but if all content in the left list is being deleted,
// keep the right list elements.
for (let childList of ["ul", "ol"]) {
  for (let otherList of ["ul", "ol"]) {
    addPromiseTest(
      `<${list}><li>list-item1[</li></${list}><${otherList}><li><${childList}><li>}list-item2</li></${childList}></li></${otherList}>`,
      {
        expectedInnerHTML: `<${list}><li>list-item1list-item2</li></${list}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > li`).firstChild,
              startOffset: gEditor.querySelector(`${list} > li`).firstChild.length,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > li > ${childList} > li`),
              endOffset: 0,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    addPromiseTest(
      `<${list}><li>[list-item1</li></${list}><${otherList}><li><${childList}><li>}list-item2</li></${childList}></li></${otherList}>`,
      {
        expectedInnerHTML: [
          `<${otherList}><li><${childList}><li>list-item2</li></${childList}></li></${otherList}>`,
          `<${otherList}><li><${childList}><li>list-item2<br></li></${childList}></li></${otherList}>`,
        ],
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > li`).firstChild,
              startOffset: 0,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > li > ${childList} > li`),
              endOffset: 0,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    addPromiseTest(
      `<${list}><li>[list-item1</li></${list}><${otherList}><li><${childList}><li>list-item2]</li></${childList}></li></${otherList}>`,
      {
        expectedInnerHTML: `<${list}><li><br></li></${list}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > li`).firstChild,
              startOffset: 0,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > li > ${childList} > li`).firstChild,
              endOffset: gEditor.querySelector(`${list} + ${otherList} > li > ${childList} > li`).firstChild.length,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    addPromiseTest(
      `<${list}><li>list-item1[</li></${list}><${otherList}><li><${childList}><li>}list-item2<br>second line of list-item2</li></${childList}></li></${otherList}>`,
      {
        expectedInnerHTML: `<${list}><li>list-item1list-item2</li></${list}><${otherList}><li><${childList}><li>second line of list-item2</li></${childList}></li></${otherList}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > li`).firstChild,
              startOffset: gEditor.querySelector(`${list} > li`).firstChild.length,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > li > ${childList} > li`),
              endOffset: 0,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    // Invalid cases.
    addPromiseTest(
      `<${list}><li>list-item1[</li></${list}><${otherList}><${childList}><li>}list-item2</li></${childList}></${otherList}>`,
      {
        expectedInnerHTML: `<${list}><li>list-item1list-item2</li></${list}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > li`).firstChild,
              startOffset: gEditor.querySelector(`${list} > li`).firstChild.length,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > ${childList} > li`),
              endOffset: 0,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    addPromiseTest(
      `<${list}><li>[list-item1</li></${list}><${otherList}><${childList}><li>}list-item2</li></${childList}></${otherList}>`,
      {
        expectedInnerHTML: [
          `<${otherList}><${childList}><li>list-item2</li></${childList}></${otherList}>`,
          `<${otherList}><${childList}><li>list-item2<br></li></${childList}></${otherList}>`,
        ],
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > li`).firstChild,
              startOffset: 0,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > ${childList} > li`),
              endOffset: 0,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    addPromiseTest(
      `<${list}><li>[list-item1</li></${list}><${otherList}><${childList}><li>list-item2]</li></${childList}></${otherList}>`,
      {
        expectedInnerHTML: `<${list}><li><br></li></${list}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > li`).firstChild,
              startOffset: 0,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > ${childList} > li`).firstChild,
              endOffset: gEditor.querySelector(`${list} + ${otherList} > ${childList} > li`).firstChild.length,
            },
          ];
        },
        expectInputEvent: true,
      }
    );

    addPromiseTest(
      `<${list}><li>list-item1[</li></${list}><${otherList}><${childList}><li>}list-item2<br>second line of list-item2</li></${childList}></${otherList}>`,
      {
        expectedInnerHTML: `<${list}><li>list-item1list-item2</li></${list}><${otherList}><${childList}><li>second line of list-item2</li></${childList}></${otherList}>`,
        expectedTargetRanges: () => {
          return [
            {
              startContainer: gEditor.querySelector(`${list} > li`).firstChild,
              startOffset: gEditor.querySelector(`${list} > li`).firstChild.length,
              endContainer: gEditor.querySelector(`${list} + ${otherList} > ${childList} > li`),
              endOffset: 0,
            },
          ];
        },
        expectInputEvent: true,
      }
    );
  }
}

</script>