public void testLogicalRangesOfUnclosedOpenTags() throws ParseException {
    HtmlParseResult result =
        parse(
            "<!DOCTYPE html>"
                + "<html>"
                + "<head>"
                + "<title>hello</title>"
                + "</head>"
                + "<body>"
                + "<table>"
                + "</html>");
    Node root = result.root();

    //        NodeUtils.dumpTree(root);

    assertNotNull(root);
    OpenTag htmlOpen = ElementUtils.query(root, "html");
    assertNotNull(htmlOpen);
    CloseTag htmlEnd = htmlOpen.matchingCloseTag();
    assertNotNull(htmlEnd);

    assertNotNull(ElementUtils.query(root, "html/head"));
    assertNotNull(ElementUtils.query(root, "html/head/title"));
    OpenTag body = ElementUtils.query(root, "html/body");
    assertNotNull(body);
    OpenTag table = ElementUtils.query(root, "html/body/table");
    assertNotNull(table);

    // both body and table should be logically closed at the beginning of the html end tag
    assertEquals(htmlEnd.from(), body.semanticEnd());
    assertEquals(htmlEnd.from(), table.semanticEnd());
  }
  public void testDivLogicalEndAtTheEOF() throws ParseException {
    String code = "<!doctype html><div><div></div>";
    //             0123456789012345678901234567890123456789
    //                                           ^
    //        NodeTreeBuilder.DEBUG = true;
    HtmlParseResult result = parse(code);
    Node root = result.root();

    //        NodeUtils.dumpTree(root);

    // the 't' node is foster parented, so it goes to the table's parent, not table itself
    OpenTag div = ElementUtils.query(root, "html/body/div");
    assertNotNull(div);

    assertEquals(30, div.semanticEnd());

    code = "<!doctype html><div><div></</div>";
    //      0123456789012345678901234567890123456789
    //                                      ^
    //        NodeTreeBuilder.DEBUG = true;
    result = parse(code);
    root = result.root();

    //        NodeUtils.dumpTree(root);

    // the 't' node is foster parented, so it goes to the table's parent, not table itself
    div = ElementUtils.query(root, "html/body/div");
    assertNotNull(div);

    assertEquals(32, div.semanticEnd());

    code = "<!doctype html><div></";
    //        0123456789012345678901234567890123456789
    //                             ^
    //        NodeTreeBuilder.DEBUG = true;
    result = parse(code);
    root = result.root();

    //        NodeUtils.dumpTree(root);

    // the 't' node is foster parented, so it goes to the table's parent, not table itself
    div = ElementUtils.query(root, "html/body/div");
    assertNotNull(div);

    assertEquals(21, div.semanticEnd());
  }
  public void testSimpleDocument() throws ParseException {
    String code =
        "<!doctype html><html><head><title>x</title></head><body><div onclick=\"alert();\"/></body></html>";
    //             012345678901234567890123456789012345678901234567890123456789012345678 901234567
    // 8901234567890123456789
    //             0         1         2         3         4         5         6          7
    // 8         9

    //        NodeTreeBuilder.DEBUG_STATES = true;
    HtmlParseResult result = parse(code);
    Node root = result.root();
    assertNotNull(root);
    //        NodeUtils.dumpTree(result.root());

    OpenTag html = ElementUtils.query(root, "html");
    assertEquals("html", html.name());
    assertEquals(15, html.from());
    assertEquals(21, html.to());
    assertEquals(15, html.from());
    assertEquals(95, html.semanticEnd());

    OpenTag body = ElementUtils.query(root, "html/body");
    assertEquals("body", body.name());
    assertEquals(50, body.from());
    assertEquals(56, body.to());
    assertEquals(50, body.from());
    assertEquals(88, body.semanticEnd());

    CloseTag bodyEndTag = body.matchingCloseTag();
    assertNotNull(bodyEndTag);
    assertSame(body, bodyEndTag.matchingOpenTag());
    assertSame(bodyEndTag, body.matchingCloseTag());

    OpenTag title = ElementUtils.query(root, "html/head/title");
    assertEquals("title", title.name());
    assertEquals(27, title.from());
    assertEquals(34, title.to());
    assertEquals(27, title.from());
    assertEquals(43, title.semanticEnd());

    CloseTag titleEndTag = title.matchingCloseTag();
    assertNotNull(titleEndTag);
    assertSame(title, titleEndTag.matchingOpenTag());
    assertSame(titleEndTag, title.matchingCloseTag());
  }