extern crate victoria_dom;
use victoria_dom::DOM;
#[test]
fn empty_vals() {
assert_eq!(DOM::new("").to_string(), "");
assert_eq!(DOM::new("").content(), "");
assert!(DOM::new("").at("p").is_none());
assert_eq!(DOM::new("").text(), "");
}
#[test]
fn nodestroy() {
// fix issue #4
let dom = DOM::new("").childs(None);
assert_eq!(dom[0].text(), "");
}
#[test]
fn basic1() {
// Simple (basics)
let dom = DOM::new(r#"
"#);
assert_eq!(dom.at("#b").unwrap().text(), "B");
assert_eq!(dom.find("div[id]").iter().map(|x| x.text()).collect::>(), ["A", "B"]);
assert_eq!(dom.at("#a").unwrap().attr("foo"), Some("0"));
assert!(dom.at("#b").unwrap().attrs().contains_key("myattr"));
assert_eq!(dom.find("[id]").iter().map(|x| x.attr("id").unwrap()).collect::>(), ["a", "b"]);
assert_eq!(dom.to_string(), r#""#);
}
#[test]
fn basic2() {
// Select based on parent
let dom = DOM::new(r#"
test1
"#);
assert_eq!(dom.find("body > div").get(0).unwrap().text(), "test1"); // right text
assert_eq!(dom.find("body > div").get(1).unwrap().text(), ""); // no content
assert_eq!(dom.find("body > div").len(), 2); // right number of elements
assert_eq!(dom.find("body > div > div").get(0).unwrap().text(), "test2"); // right text
assert_eq!(dom.find("body > div > div").len(), 1); // right number of elements
}
#[test]
fn basic3() {
// Basic navigation
let dom = DOM::new(r#"
test
easy
works well
< very broken
more text
"#);
assert!(dom.tag().is_none()); // no tag
assert!(!dom.attrs().contains_key("foo"));
assert_eq!(
dom.to_string(),
r#"
test
easy
works well
< very broken
more text
"#);
let simple = dom.at("foo simple.working[class^=\"wor\"]").unwrap();
assert_eq!(simple.parent().unwrap().text_all(), "test easy works well yada yada < very broken more text");
assert_eq!(simple.tag().unwrap(), "simple");
assert_eq!(simple.attr("class").unwrap(), "working");
assert_eq!(simple.text(), "easy");
assert_eq!(simple.parent().unwrap().tag().unwrap(), "foo");
assert_eq!(simple.parent().unwrap().attr("bar").unwrap(), "baeasy");
assert_eq!(dom.at("test#test").unwrap().tag().unwrap(), "test");
assert_eq!(dom.at("[class$=\"ing\"]").unwrap().tag().unwrap(), "simple");
assert_eq!(dom.at("[class$=ing]").unwrap().tag().unwrap(), "simple");
assert_eq!(dom.at("[class=\"working\"]").unwrap().tag().unwrap(), "simple");
assert_eq!(dom.at("[class=working][class]").unwrap().tag().unwrap(), "simple");
assert_eq!(dom.at("foo > simple").unwrap().next().unwrap().tag().unwrap(), "test");
assert_eq!(dom.at("foo > simple").unwrap().next().unwrap().next().unwrap().tag().unwrap(), "a");
assert_eq!(dom.at("foo > test").unwrap().prev().unwrap().tag().unwrap(), "simple");
assert!(dom.next().is_none());
assert!(dom.prev().is_none());
assert!(dom.at("foo > a").unwrap().next().is_none());
assert!(dom.at("foo > simple").unwrap().prev().is_none());
assert_eq!(dom.at("simple").unwrap().ancestors(None).iter().map(|x| x.tag().unwrap()).collect::>(), ["foo"]);
}
#[test]
fn class_and_id() {
// Class and ID
let dom = DOM::new(r#"a
"#);
assert_eq!(dom.at("div#id.class").unwrap().text(), "a");
}
#[test]
fn deep_nesting() {
// Deep nesting (parent combinator)
let dom = DOM::new(r#"
Foo
"#);
let p = dom.find("body > #container > div p[id]");
assert_eq!(p.len(), 1);
assert_eq!(p.get(0).unwrap().attr("id").unwrap(), "foo");
assert_eq!(
dom.find("div").iter().map(|x| x.attr("id").unwrap()).collect::>(),
["container", "header", "logo", "buttons", "buttons", "content"]
);
assert_eq!(
dom.find("p").iter().map(|x| x.attr("id").unwrap()).collect::>(),
["foo", "bar"]
);
assert_eq!(
dom.at("p").unwrap().ancestors(None).iter().map(|x| x.tag().unwrap()).collect::>(),
["div", "div", "div", "body", "html"]
);
assert_eq!(dom.at("html").unwrap().ancestors(None).len(), 0);
assert_eq!(dom.ancestors(None).len(), 0);
}
#[test]
fn script_tag() {
let dom = DOM::new(r#""#);
assert_eq!(dom.at("script").unwrap().text(), "alert('world');");
}
#[test]
fn html5_base() {
// HTML5 (unquoted values)
let dom = DOM::new(r#"works
"#);
assert_eq!(dom.at("#test").unwrap().text(), "works");
assert_eq!(dom.at("div").unwrap().text(), "works");
assert_eq!(dom.at("[foo=bar][foo=\"bar\"]").unwrap().text(), "works");
assert!(dom.at("[foo=\"ba\"]").is_none());
assert_eq!(dom.at("[foo=bar]").unwrap().text(), "works");
assert!(dom.at("[foo=ba]").is_none());
assert_eq!(dom.at(".tset").unwrap().text(), "works");
assert_eq!(dom.at("[bar=/baz/]").unwrap().text(), "works");
assert_eq!(dom.at("[baz=//]").unwrap().text(), "works");
}
#[test]
fn html1_mix() {
// HTML1 (single quotes, uppercase tags and whitespace in attributes)
let dom = DOM::new(r#"works
"#);
assert_eq!(dom.at("#test").unwrap().text(), "works");
assert_eq!(dom.at("div").unwrap().text(), "works");
assert_eq!(dom.at("[foo=\"bar\"]").unwrap().text(), "works");
assert!(dom.at("[foo=\"ba\"]").is_none());
assert_eq!(dom.at("[foo=bar]").unwrap().text(), "works");
assert!(dom.at("[foo=ba]").is_none());
assert_eq!(dom.at(".tset").unwrap().text(), "works");
}
#[test]
fn unicode_snowman() {
// Already decoded Unicode snowman and quotes in selector
let dom = DOM::new(r#"☃
"#);
assert_eq!(dom.at(r#"[id="snow'm\"an"]"#).unwrap().text(), "☃");
assert_eq!(dom.at(r#"[id="snow'm\22 an"]"#).unwrap().text(), "☃");
assert_eq!(dom.at(r#"[id="snow\'m\000022an"]"#).unwrap().text(), "☃");
assert_eq!(dom.at("[id='snow\\'m\"an']").unwrap().text(), "☃");
assert_eq!(dom.at("[id='snow\\27m\"an']").unwrap().text(), "☃");
assert!(dom.at(r#"[id="snow'm\22an"]"#).is_none());
assert!(dom.at(r#"[id="snow'm\21 an"]"#).is_none());
assert!(dom.at(r#"[id="snow'm\000021an"]"#).is_none());
assert!(dom.at(r#"[id="snow'm\000021 an"]"#).is_none());
}
#[test]
fn unicode_selectors() {
// Unicode and escaped selectors
let html = r#"Snowman
Heart
"#;
let dom = DOM::new(html);
assert_eq!(dom.at("#\\\n\\002603x").unwrap().text(), "Snowman");
assert_eq!(dom.at("#\\2603 x").unwrap().text(), "Snowman");
assert_eq!(dom.at("#\\\n\\2603 x").unwrap().text(), "Snowman");
assert_eq!(dom.at("[id=\"\\\n\\2603 x\"]").unwrap().text(), "Snowman");
assert_eq!(dom.at("[id=\"\\\n\\002603x\"]").unwrap().text(), "Snowman");
assert_eq!(dom.at("[id=\"\\\\2603 x\"]").unwrap().text(), "Snowman");
assert_eq!(dom.at("html #\\\n\\002603x").unwrap().text(), "Snowman");
assert_eq!(dom.at("html #\\2603 x").unwrap().text(), "Snowman");
assert_eq!(dom.at("html #\\\n\\2603 x").unwrap().text(), "Snowman");
assert_eq!(dom.at("html [id=\"\\\n\\2603 x\"]").unwrap().text(), "Snowman");
assert_eq!(dom.at("html [id=\"\\\n\\002603x\"]").unwrap().text(), "Snowman");
assert_eq!(dom.at("html [id=\"\\\\2603 x\"]").unwrap().text(), "Snowman");
assert_eq!(dom.at("#☃x").unwrap().text(), "Snowman");
assert_eq!(dom.at("html div#☃x").unwrap().text(), "Snowman");
assert_eq!(dom.at("[id^=\"☃\"]").unwrap().text(), "Snowman");
assert_eq!(dom.at("div[id^=\"☃\"]").unwrap().text(), "Snowman");
assert_eq!(dom.at("html div[id^=\"☃\"]").unwrap().text(), "Snowman");
assert_eq!(dom.at("html > div[id^=\"☃\"]").unwrap().text(), "Snowman");
assert_eq!(dom.at("[id^=☃]").unwrap().text(), "Snowman");
assert_eq!(dom.at("div[id^=☃]").unwrap().text(), "Snowman");
assert_eq!(dom.at("html div[id^=☃]").unwrap().text(), "Snowman");
assert_eq!(dom.at("html > div[id^=☃]").unwrap().text(), "Snowman");
assert_eq!(dom.at(".\\\n\\002665").unwrap().text(), "Heart");
assert_eq!(dom.at(".\\2665").unwrap().text(), "Heart");
assert_eq!(dom.at("html .\\\n\\002665").unwrap().text(), "Heart");
assert_eq!(dom.at("html .\\2665").unwrap().text(), "Heart");
assert_eq!(dom.at("html [class$=\"\\\n\\002665\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("html [class$=\"\\2665\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("[class$=\"\\\n\\002665\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("[class$=\"\\2665\"]").unwrap().text(), "Heart");
assert_eq!(dom.at(".x").unwrap().text(), "Heart");
assert_eq!(dom.at("html .x").unwrap().text(), "Heart");
assert_eq!(dom.at(".♥").unwrap().text(), "Heart");
assert_eq!(dom.at("html .♥").unwrap().text(), "Heart");
assert_eq!(dom.at("div.♥").unwrap().text(), "Heart");
assert_eq!(dom.at("html div.♥").unwrap().text(), "Heart");
assert_eq!(dom.at("[class$=\"♥\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("div[class$=\"♥\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("html div[class$=\"♥\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("html > div[class$=\"♥\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("[class$=♥]").unwrap().text(), "Heart");
assert_eq!(dom.at("div[class$=♥]").unwrap().text(), "Heart");
assert_eq!(dom.at("html div[class$=♥]").unwrap().text(), "Heart");
assert_eq!(dom.at("html > div[class$=♥]").unwrap().text(), "Heart");
assert_eq!(dom.at("[class~=\"♥\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("div[class~=\"♥\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("html div[class~=\"♥\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("html > div[class~=\"♥\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("[class~=♥]").unwrap().text(), "Heart");
assert_eq!(dom.at("div[class~=♥]").unwrap().text(), "Heart");
assert_eq!(dom.at("html div[class~=♥]").unwrap().text(), "Heart");
assert_eq!(dom.at("html > div[class~=♥]").unwrap().text(), "Heart");
assert_eq!(dom.at("[class~=\"x\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("div[class~=\"x\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("html div[class~=\"x\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("html > div[class~=\"x\"]").unwrap().text(), "Heart");
assert_eq!(dom.at("[class~=x]").unwrap().text(), "Heart");
assert_eq!(dom.at("div[class~=x]").unwrap().text(), "Heart");
assert_eq!(dom.at("html div[class~=x]").unwrap().text(), "Heart");
assert_eq!(dom.at("html > div[class~=x]").unwrap().text(), "Heart");
assert_eq!(dom.at("html").unwrap().to_string(), html);
assert_eq!(dom.at("#☃x").unwrap().parent().unwrap().to_string(), html);
assert_eq!(dom.to_string(), html);
assert_eq!(dom.content(), html);
let dom = DOM::new(r#"☃♥☃"#);
assert_eq!(dom.at("title").unwrap().text(), "♥");
assert_eq!(dom.at("*").unwrap().text(), "♥");
assert_eq!(dom.at(".test").unwrap().text(), "♥");
}
#[test]
fn attrs_on_multiple_lines() {
// Attributes on multiple lines
let dom = DOM::new("");
assert_eq!(dom.at("div.x").unwrap().attr("test").unwrap(), "23");
assert_eq!(dom.at("[foo=\"bar\"]").unwrap().attr("class").unwrap(), "x");
}
#[test]
fn markup_chars_in_attr_vals() {
// Markup characters in attribute values
let dom = DOM::new("");
assert_eq!(dom.at("div[id=\"\"]").unwrap().attrs().get("test").unwrap().clone(), Some("=".to_owned()));
assert_eq!(dom.at("[id=\"\"]").unwrap().text(), "Test");
assert_eq!(dom.at("[id=\"><\"]").unwrap().attrs().get("id").unwrap().clone(), Some("><".to_owned()));
}
#[test]
fn empty_attrs() {
// Empty attributes
let dom = DOM::new("");
assert_eq!(dom.at("div").unwrap().attr("test").unwrap(), "");
assert_eq!(dom.at("div").unwrap().attr("test2").unwrap(), "");
assert_eq!(dom.at("[test]").unwrap().tag().unwrap(), "div");
assert_eq!(dom.at("[test=\"\"]").unwrap().tag().unwrap(), "div");
assert_eq!(dom.at("[test2]").unwrap().tag().unwrap(), "div");
assert_eq!(dom.at("[test2=\"\"]").unwrap().tag().unwrap(), "div");
assert!(dom.at("[test3]").is_none());
assert!(dom.at("[test3=\"\"]").is_none());
}
#[test]
fn multi_line_attr() {
// Multi-line attribute
let dom = DOM::new("");
assert_eq!(dom.at("div").unwrap().attr("class").unwrap(), "line1\nline2");
assert_eq!(dom.at(".line1").unwrap().tag().unwrap(), "div");
assert_eq!(dom.at(".line2").unwrap().tag().unwrap(), "div");
assert!(dom.at(".line3").is_none());
}
#[test]
fn entities_in_attrs() {
assert_eq!(DOM::new("").at("a").unwrap().attr("href").unwrap(), "/?foo<=bar");
assert_eq!(DOM::new("").at("a").unwrap().attr("href").unwrap(), "/?f<oo=bar");
assert_eq!(DOM::new("").at("a").unwrap().attr("href").unwrap(), "/?f<-oo=bar");
assert_eq!(DOM::new("").at("a").unwrap().attr("href").unwrap(), "/?foo=<");
assert_eq!(DOM::new("").at("a").unwrap().attr("href").unwrap(), "/?fcontent");
assert!(dom.at("div").is_some());
assert_eq!(dom.at("div").unwrap().text(), "content");
}
#[test]
fn class_with_hyphen() {
// Class with hyphen
let dom = DOM::new(r#"A
A1
"#);
assert_eq!(dom.find(".a").iter().map(|x| x.text()).collect::>(), ["A"]); // found first element only
assert_eq!(dom.find(".a-1").iter().map(|x| x.text()).collect::>(), ["A1"]); // found last element only
}
#[test]
fn empty_tags() {
// Empty tags
let dom = DOM::new("
");
assert_eq!(dom.to_string(), "
");
}
#[test]
fn inner_html() {
let dom = DOM::new("xxxxxxx");
assert_eq!(dom.at("a").unwrap().content(), "xxxxxxx");
assert_eq!(dom.content(), "xxxxxxx");
}
#[test]
fn multiple_selectors() {
// Multiple selectors
let dom = DOM::new("A
B
C
D
");
assert_eq!(dom.find("p, div").iter().map(|x| x.text()).collect::>(), ["A", "B", "C", "D"]);
assert_eq!(dom.find("#a, #c").iter().map(|x| x.text()).collect::>(), ["A", "C"]);
assert_eq!(dom.find("div#a, div#b").iter().map(|x| x.text()).collect::>(), ["A", "B"]);
assert_eq!(dom.find("div[id=\"a\"], div[id=\"c\"]").iter().map(|x| x.text()).collect::>(), ["A", "C"]);
let dom2 = DOM::new("A
B
C
");
assert_eq!(dom2.find("#☃, #♥x").iter().map(|x| x.text()).collect::>(), ["A", "C"]);
assert_eq!(dom2.find("div#☃, div#b").iter().map(|x| x.text()).collect::>(), ["A", "B"]);
assert_eq!(dom2.find("div[id=\"☃\"], div[id=\"♥x\"]").iter().map(|x| x.text()).collect::>(), ["A", "C"]);
}
#[test]
fn multiple_attributes() {
// Multiple attributes
let dom = DOM::new(r#"
A
B
C
D
"#);
assert_eq!(dom.find("div[foo=\"bar\"][bar=\"baz\"]").iter().map(|x| x.text()).collect::>(), ["A", "C"]);
assert_eq!(dom.find("div[foo^=\"b\"][foo$=\"r\"]").iter().map(|x| x.text()).collect::>(), ["A", "B", "C"]);
assert!(dom.at("[foo=\"bar\"]").unwrap().prev().is_none());
assert_eq!(dom.at("[foo=\"bar\"]").unwrap().next().unwrap().text(), "B");
assert_eq!(dom.at("[foo=\"bar\"]").unwrap().next().unwrap().prev().unwrap().text(), "A");
assert!(dom.at("[foo=\"bar\"]").unwrap().next().unwrap().next().unwrap().next().unwrap().next().is_none());
}
#[test]
fn pseudo_classes() {
// Pseudo-classes
let dom = DOM::new(r#"
"#);
assert_eq!(dom.find(":root").len(), 1);
assert_eq!(dom.find(":root").get(0).unwrap().tag(), Some("form"));
assert_eq!(dom.find("*:root").get(0).unwrap().tag(), Some("form"));
assert_eq!(dom.find("form:root").get(0).unwrap().tag(), Some("form"));
assert_eq!(dom.find(":checked").len(), 4);
assert_eq!(dom.find(":checked").get(0).unwrap().attr("name").unwrap(), "groovy");
assert_eq!(dom.find("option:checked").get(0).unwrap().attr("value").unwrap(), "e");
assert_eq!(dom.find(":checked").get(1).unwrap().text(), "E");
assert_eq!(dom.find("*:checked").get(1).unwrap().text(), "E");
assert_eq!(dom.find(":checked").get(2).unwrap().text(), "H");
assert_eq!(dom.find(":checked").get(3).unwrap().attr("name").unwrap(), "I");
assert_eq!(dom.find("option[selected]").len(), 2);
assert_eq!(dom.find("option[selected]").get(0).unwrap().attr("value").unwrap(), "e");
assert_eq!(dom.find("option[selected]").get(1).unwrap().text(), "H");
assert_eq!(dom.find(":checked[value=\"e\"]").get(0).unwrap().text(), "E");
assert_eq!(dom.find("*:checked[value=\"e\"]").get(0).unwrap().text(), "E");
assert_eq!(dom.find("option:checked[value=\"e\"]").get(0).unwrap().text(), "E");
assert_eq!(dom.at("optgroup option:checked[value=\"e\"]").unwrap().text(), "E");
assert_eq!(dom.at("select option:checked[value=\"e\"]").unwrap().text(), "E");
assert_eq!(dom.at("select :checked[value=\"e\"]").unwrap().text(), "E");
assert_eq!(dom.at("optgroup > :checked[value=\"e\"]").unwrap().text(), "E");
assert_eq!(dom.at("select *:checked[value=\"e\"]").unwrap().text(), "E");
assert_eq!(dom.at("optgroup > *:checked[value=\"e\"]").unwrap().text(), "E");
assert_eq!(dom.find(":checked[value=\"e\"]").len(), 1);
assert_eq!(dom.find(":empty").get(0).unwrap().attr("name").unwrap(), "user");
assert_eq!(dom.find("input:empty").get(0).unwrap().attr("name").unwrap(), "user");
assert_eq!(dom.at(":empty[type^=\"ch\"]").unwrap().attr("name").unwrap(), "groovy");
assert_eq!(dom.at("p").unwrap().attr("id").unwrap(), "content");
assert_eq!(dom.at("p:empty").unwrap().attr("id").unwrap(), "no_content");
// More pseudo-classes
let dom = DOM::new("
");
assert_eq!(dom.find("li:nth-child(odd)").iter().map(|x| x.text()).collect::>(), ["A", "C", "E", "G"]);
assert_eq!(dom.find("li:NTH-CHILD(ODD)").iter().map(|x| x.text()).collect::>(), ["A", "C", "E", "G"]);
assert_eq!(dom.find("li:nth-last-child(odd)").iter().map(|x| x.text()).collect::>(), ["B", "D", "F", "H"]);
assert_eq!(dom.find(":nth-child(odd)").get(0).unwrap().tag().unwrap(), "ul");
assert_eq!(dom.find(":nth-child(odd)").get(1).unwrap().text(), "A");
assert_eq!(dom.find(":nth-child(1)").get(0).unwrap().tag().unwrap(), "ul");
assert_eq!(dom.find(":nth-child(1)").get(1).unwrap().text(), "A");
assert_eq!(dom.find(":nth-last-child(odd)").get(0).unwrap().tag().unwrap(), "ul");
assert_eq!(dom.find(":nth-last-child(odd)").last().unwrap().text(), "H");
assert_eq!(dom.find(":nth-last-child(1)").get(0).unwrap().tag().unwrap(), "ul");
assert_eq!(dom.find(":nth-last-child(1)").get(1).unwrap().text(), "H");
assert_eq!(dom.find("li:nth-child(2n+1)").iter().map(|x| x.text()).collect::>(), ["A", "C", "E", "G"]);
assert_eq!(dom.find("li:nth-child(2n + 1)").iter().map(|x| x.text()).collect::>(), ["A", "C", "E", "G"]);
assert_eq!(dom.find("li:nth-last-child(2n+1)").iter().map(|x| x.text()).collect::>(), ["B", "D", "F", "H"]);
assert_eq!(dom.find("li:nth-child(even)").iter().map(|x| x.text()).collect::>(), ["B", "D", "F", "H"]);
assert_eq!(dom.find("li:NTH-CHILD(EVEN)").iter().map(|x| x.text()).collect::>(), ["B", "D", "F", "H"]);
assert_eq!(dom.find("li:nth-last-child( even )").iter().map(|x| x.text()).collect::>(), ["A", "C", "E", "G"]);
assert_eq!(dom.find("li:nth-child(2n+2)").iter().map(|x| x.text()).collect::>(), ["B", "D", "F", "H"]);
assert_eq!(dom.find("li:nTh-chILd(2N+2)").iter().map(|x| x.text()).collect::>(), ["B", "D", "F", "H"]);
assert_eq!(dom.find("li:nth-child( 2n + 2 )").iter().map(|x| x.text()).collect::>(), ["B", "D", "F", "H"]);
assert_eq!(dom.find("li:nth-last-child(2n+2)").iter().map(|x| x.text()).collect::>(), ["A", "C", "E", "G"]);
assert_eq!(dom.find("li:nth-child(4n+1)").iter().map(|x| x.text()).collect::>(), ["A", "E"]);
assert_eq!(dom.find("li:nth-last-child(4n+1)").iter().map(|x| x.text()).collect::>(), ["D", "H"]);
assert_eq!(dom.find("li:nth-child(4n+4)").iter().map(|x| x.text()).collect::>(), ["D", "H"]);
assert_eq!(dom.find("li:nth-last-child(4n+4)").iter().map(|x| x.text()).collect::>(), ["A", "E"]);
assert_eq!(dom.find("li:nth-child(4n)").iter().map(|x| x.text()).collect::>(), ["D", "H"]);
assert_eq!(dom.find("li:nth-child( 4n )").iter().map(|x| x.text()).collect::>(), ["D", "H"]);
assert_eq!(dom.find("li:nth-last-child(4n)").iter().map(|x| x.text()).collect::>(), ["A", "E"]);
assert_eq!(dom.find("li:nth-child(5n-2)").iter().map(|x| x.text()).collect::>(), ["C", "H"]);
assert_eq!(dom.find("li:nth-child( 5n - 2 )").iter().map(|x| x.text()).collect::>(), ["C", "H"]);
assert_eq!(dom.find("li:nth-last-child(5n-2)").iter().map(|x| x.text()).collect::>(), ["A", "F"]);
assert_eq!(dom.find("li:nth-child(-n+3)").iter().map(|x| x.text()).collect::>(), ["A", "B", "C"]);
assert_eq!(dom.find("li:nth-child( -n + 3 )").iter().map(|x| x.text()).collect::>(), ["A", "B", "C"]);
assert_eq!(dom.find("li:nth-last-child(-n+3)").iter().map(|x| x.text()).collect::>(), ["F", "G", "H"]);
assert_eq!(dom.find("li:nth-child(-1n+3)").iter().map(|x| x.text()).collect::>(), ["A", "B", "C"]);
assert_eq!(dom.find("li:nth-last-child(-1n+3)").iter().map(|x| x.text()).collect::>(), ["F", "G", "H"]);
assert_eq!(dom.find("li:nth-child(3n)").iter().map(|x| x.text()).collect::>(), ["C", "F"]);
assert_eq!(dom.find("li:nth-last-child(3n)").iter().map(|x| x.text()).collect::>(), ["C", "F"]);
assert_eq!(dom.find("li:NTH-LAST-CHILD(3N)").iter().map(|x| x.text()).collect::>(), ["C", "F"]);
assert_eq!(dom.find("li:Nth-Last-Child(3N)").iter().map(|x| x.text()).collect::>(), ["C", "F"]);
assert_eq!(dom.find("li:nth-child( 3 )").iter().map(|x| x.text()).collect::>(), ["C"]);
assert_eq!(dom.find("li:nth-last-child( +3 )").iter().map(|x| x.text()).collect::>(), ["F"]);
assert_eq!(dom.find("li:nth-child(1n+0)").iter().map(|x| x.text()).collect::>(), ["A", "B", "C", "D", "E", "F", "G"]);
assert_eq!(dom.find("li:nth-child(1n-0)").iter().map(|x| x.text()).collect::>(), ["A", "B", "C", "D", "E", "F", "G"]);
assert_eq!(dom.find("li:nth-child(n+0)").iter().map(|x| x.text()).collect::>(), ["A", "B", "C", "D", "E", "F", "G"]);
assert_eq!(dom.find("li:nth-child(n)").iter().map(|x| x.text()).collect::>(), ["A", "B", "C", "D", "E", "F", "G"]);
assert_eq!(dom.find("li:nth-child(n+0)").iter().map(|x| x.text()).collect::>(), ["A", "B", "C", "D", "E", "F", "G"]);
assert_eq!(dom.find("li:NTH-CHILD(N+0)").iter().map(|x| x.text()).collect::>(), ["A", "B", "C", "D", "E", "F", "G"]);
assert_eq!(dom.find("li:Nth-Child(N+0)").iter().map(|x| x.text()).collect::>(), ["A", "B", "C", "D", "E", "F", "G"]);
assert_eq!(dom.find("li:nth-child(n)").iter().map(|x| x.text()).collect::>(), ["A", "B", "C", "D", "E", "F", "G"]);
assert_eq!(dom.find("li:nth-child(0n+1)").iter().map(|x| x.text()).collect::>(), ["A"]);
assert_eq!(dom.find("li:nth-child(0n+0)").len(), 0);
assert_eq!(dom.find("li:nth-child(0)").len(), 0);
assert_eq!(dom.find("li:nth-child()").len(), 0);
assert_eq!(dom.find("li:nth-child(whatever)").len(), 0);
assert_eq!(dom.find("li:whatever(whatever)").len(), 0);
// Even more pseudo-classes
let dom = DOM::new(r#"
"#);
assert_eq!(dom.find("ul :nth-child(odd)").iter().map(|x| x.text()).collect::>(), ["A", "C", "E", "G", "I"]);
assert_eq!(dom.find("li:nth-of-type(odd)").iter().map(|x| x.text()).collect::>(), ["A", "E", "H"]);
assert_eq!(dom.find("li:nth-last-of-type( odd )").iter().map(|x| x.text()).collect::>(), ["C", "F", "I"]);
assert_eq!(dom.find("p:nth-of-type(odd)").iter().map(|x| x.text()).collect::>(), ["B", "G"]);
assert_eq!(dom.find("p:nth-last-of-type(odd)").iter().map(|x| x.text()).collect::>(), ["B", "G"]);
assert_eq!(dom.find("ul :nth-child(1)").iter().map(|x| x.text()).collect::>(), ["A"]);
assert_eq!(dom.find("ul :first-child").iter().map(|x| x.text()).collect::>(), ["A"]);
assert_eq!(dom.find("p:nth-of-type(1)").iter().map(|x| x.text()).collect::>(), ["B"]);
assert_eq!(dom.find("p:first-of-type").iter().map(|x| x.text()).collect::>(), ["B"]);
assert_eq!(dom.find("li:nth-of-type(1)").iter().map(|x| x.text()).collect::>(), ["A"]);
assert_eq!(dom.find("li:first-of-type").iter().map(|x| x.text()).collect::>(), ["A"]);
assert_eq!(dom.find("ul :nth-last-child(-n+1)").iter().map(|x| x.text()).collect::>(), ["I"]);
assert_eq!(dom.find("ul :last-child").iter().map(|x| x.text()).collect::>(), ["I"]);
assert_eq!(dom.find("p:nth-last-of-type(-n+1)").iter().map(|x| x.text()).collect::>(), ["G"]);
assert_eq!(dom.find("p:last-of-type").iter().map(|x| x.text()).collect::>(), ["G"]);
assert_eq!(dom.find("li:nth-last-of-type(-n+1)").iter().map(|x| x.text()).collect::>(), ["I"]);
assert_eq!(dom.find("li:last-of-type").iter().map(|x| x.text()).collect::>(), ["I"]);
assert_eq!(dom.find("ul :nth-child(-n+3):not(li)").iter().map(|x| x.text()).collect::>(), ["B"]);
assert_eq!(dom.find("ul :nth-child(-n+3):NOT(li)").iter().map(|x| x.text()).collect::>(), ["B"]);
assert_eq!(dom.find("ul :nth-child(-n+3):not(:first-child)").iter().map(|x| x.text()).collect::>(), ["B", "C"]);
assert_eq!(dom.find("ul :nth-child(-n+3):not(.♥)").iter().map(|x| x.text()).collect::>(), ["A", "B"]);
assert_eq!(dom.find("ul :nth-child(-n+3):not([class$=\"♥\"])").iter().map(|x| x.text()).collect::>(), ["A", "B"]);
assert_eq!(dom.find("ul :nth-child(-n+3):not(li[class$=\"♥\"])").iter().map(|x| x.text()).collect::>(), ["A", "B"]);
assert_eq!(dom.find("ul :nth-child(-n+3):not([class$=\"♥\"][class^=\"test\"])").iter().map(|x| x.text()).collect::>(), ["A", "B"]);
assert_eq!(dom.find("ul :nth-child(-n+3):not(*[class$=\"♥\"])").iter().map(|x| x.text()).collect::>(), ["A", "B"]);
assert_eq!(dom.find("ul :nth-child(-n+3):not(:nth-child(-n+2))").iter().map(|x| x.text()).collect::>(), ["C"]);
assert_eq!(dom.find("ul :nth-child(-n+3):not(:nth-child(1)):not(:nth-child(2))").iter().map(|x| x.text()).collect::>(), ["C"]);
assert_eq!(dom.find(":only-child").iter().map(|x| x.text()).collect::>(), ["J"]);
assert_eq!(dom.find("div :only-of-type").iter().map(|x| x.text()).collect::>(), ["J", "K"]);
assert_eq!(dom.find("div:only-child").iter().map(|x| x.text()).collect::>(), ["J"]);
assert_eq!(dom.find("div div:only-of-type").iter().map(|x| x.text()).collect::>(), ["J", "K"]);
}