#![allow(clippy::result_unit_err)]
mod attr_value;
mod brik_selectors;
mod element_impl;
mod local_name_selector;
mod pseudo_class;
mod pseudo_element;
mod selector;
mod selector_context;
mod selectors;
mod specificity;
pub use attr_value::AttrValue;
pub use brik_selectors::BrikSelectors;
pub use local_name_selector::LocalNameSelector;
pub use pseudo_class::PseudoClass;
pub use pseudo_element::PseudoElement;
pub use selector::Selector;
pub use selector_context::SelectorContext;
pub use selectors::Selectors;
pub use specificity::Specificity;
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parse_html;
use crate::traits::*;
use html5ever::local_name;
#[cfg(feature = "namespaces")]
use html5ever::{ns, Namespace};
#[test]
#[cfg(feature = "namespaces")]
fn namespace_type_selector() {
let html = r#"<!DOCTYPE html>
<html>
<body>
<div>HTML div</div>
<svg xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100"/>
<circle cx="50" cy="50" r="40"/>
</svg>
</body>
</html>"#;
let document = parse_html().one(html);
let mut context = SelectorContext::new();
context.add_namespace("svg".to_string(), ns!(svg));
let selectors = Selectors::compile_with_context("svg|rect", &context).unwrap();
let rects = selectors
.filter(document.descendants().elements())
.collect::<Vec<_>>();
assert_eq!(rects.len(), 1);
assert_eq!(rects[0].name.local, local_name!("rect"));
assert_eq!(rects[0].name.ns, ns!(svg));
let selectors = Selectors::compile_with_context("svg|*", &context).unwrap();
let svg_elements = selectors
.filter(document.descendants().elements())
.collect::<Vec<_>>();
assert_eq!(svg_elements.len(), 3); }
#[test]
#[cfg(feature = "namespaces")]
fn namespace_attribute_selector() {
let html = r##"<!DOCTYPE html>
<html xmlns:custom="http://example.com/custom">
<body>
<div>Regular div without custom attribute</div>
<div custom:attr="value">Div with custom:attr</div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="#icon"/>
</svg>
</body>
</html>"##;
let document = parse_html().one(html);
let mut context = SelectorContext::new();
context.add_namespace(
"xlink".to_string(),
Namespace::from("http://www.w3.org/1999/xlink"),
);
let selectors = Selectors::compile_with_context("[xlink|href]", &context).unwrap();
let elements = selectors
.filter(document.descendants().elements())
.collect::<Vec<_>>();
assert_eq!(elements.len(), 1);
assert_eq!(elements[0].name.local, local_name!("use"));
}
#[test]
#[cfg(feature = "namespaces")]
fn namespace_selector_undefined_prefix() {
let context = SelectorContext::new();
let result = Selectors::compile_with_context("undefined|div", &context);
assert!(
result.is_err(),
"Should fail with undefined namespace prefix"
);
}
#[test]
fn namespace_selector_backward_compatibility() {
let html = r#"<div class="test">Content</div>"#;
let document = parse_html().one(html);
let selectors = Selectors::compile("div.test").unwrap();
let elements = selectors
.filter(document.descendants().elements())
.collect::<Vec<_>>();
assert_eq!(elements.len(), 1);
}
#[test]
#[cfg(feature = "namespaces")]
fn namespace_context_builder_pattern() {
let mut context = SelectorContext::new();
context
.add_namespace("svg".to_string(), ns!(svg))
.add_namespace("html".to_string(), ns!(html))
.set_default_namespace(ns!(html));
let html = r#"<!DOCTYPE html>
<html>
<body>
<svg xmlns="http://www.w3.org/2000/svg">
<rect/>
</svg>
</body>
</html>"#;
let document = parse_html().one(html);
let selectors = Selectors::compile_with_context("svg|rect", &context).unwrap();
let rects = selectors
.filter(document.descendants().elements())
.collect::<Vec<_>>();
assert_eq!(rects.len(), 1);
}
#[test]
fn select() {
let html = r"
<title>Test case</title>
<p class=foo>Foo
<p>Bar
<p class=foo>Foo
";
let document = parse_html().one(html);
let matching = document.select("p.foo").unwrap().collect::<Vec<_>>();
assert_eq!(matching.len(), 2);
let child = matching[0].as_node().first_child().unwrap();
assert_eq!(&**child.as_text().unwrap().borrow(), "Foo\n");
assert_eq!(matching[0].attributes.borrow().get("class"), Some("foo"));
assert_eq!(
matching[0].attributes.borrow().get(local_name!("class")),
Some("foo")
);
let selectors = Selectors::compile("p.foo").unwrap();
let matching2 = selectors
.filter(document.descendants().elements())
.collect::<Vec<_>>();
assert_eq!(matching, matching2);
}
#[test]
fn select_first() {
let html = r"
<title>Test case</title>
<p class=foo>Foo
<p>Bar
<p class=foo>Baz
";
let document = parse_html().one(html);
let matching = document.select_first("p.foo").unwrap();
let child = matching.as_node().first_child().unwrap();
assert_eq!(&**child.as_text().unwrap().borrow(), "Foo\n");
assert_eq!(matching.attributes.borrow().get("class"), Some("foo"));
assert_eq!(
matching.attributes.borrow().get(local_name!("class")),
Some("foo")
);
assert!(document.select_first("p.bar").is_err());
}
#[test]
fn specificity() {
let selectors = Selectors::compile(".example, :first-child, div").unwrap();
let specificities = selectors
.0
.iter()
.map(|s| s.specificity())
.collect::<Vec<_>>();
assert_eq!(specificities.len(), 3);
assert!(specificities[0] == specificities[1]);
assert!(specificities[0] > specificities[2]);
assert!(specificities[1] > specificities[2]);
}
}