use crate::{Document, blocks::IsBlock};
#[track_caller]
pub(crate) fn assert_xpath(doc: &Document, xpath: &str, expected_count: usize) {
let vdom = doc.to_virtual_dom();
let matches = query_xpath(&vdom, xpath);
assert_eq!(
matches.len(),
expected_count,
"XPath query '{}' expected {} matches, found {}",
xpath,
expected_count,
matches.len()
);
}
#[track_caller]
pub(crate) fn assert_css(doc: &Document, selector: &str, expected_count: usize) {
let vdom = doc.to_virtual_dom();
let matches = query_css(&vdom, selector);
assert_eq!(
matches.len(),
expected_count,
"CSS query '{}' expected {} matches, found {}",
selector,
expected_count,
matches.len()
);
}
pub(crate) fn assert_output_contains<'src>(doc: &'src Document<'src>, text: &str) {
if !check_block_contains(doc, text) {
panic!("Document should have contained {text}, but did not:\n\n{doc:#?}");
}
}
fn check_block_contains<'src, B: IsBlock<'src>>(block: &'src B, text: &str) -> bool {
if let Some(content) = block.rendered_content()
&& content.contains(text)
{
return true;
}
block.nested_blocks().any(|b| check_block_contains(b, text))
}
pub(crate) fn refute_output_contains<'src>(doc: &'src Document<'src>, text: &str) {
refute_block_contains(doc, text);
}
fn refute_block_contains<'src, B: IsBlock<'src>>(block: &'src B, text: &str) {
if let Some(content) = block.rendered_content() {
dbg!(&content);
if content.contains(text) {
panic!("Block should not have contained {text}, but did:\n\n{block:#?}");
}
}
for nested_block in block.nested_blocks() {
refute_block_contains(nested_block, text);
}
}
#[track_caller]
pub(crate) fn assert_rendered_contains(doc: &Document, text: &str) {
let rendered = rendered_text(doc);
assert!(
rendered.contains(text),
"rendered output should contain {text:?}, but did not:\n\n{rendered}"
);
}
#[track_caller]
pub(crate) fn refute_rendered_contains(doc: &Document, text: &str) {
let rendered = rendered_text(doc);
assert!(
!rendered.contains(text),
"rendered output should not contain {text:?}, but did:\n\n{rendered}"
);
}
fn rendered_text(doc: &Document) -> String {
fn collect(node: &virtual_dom::VirtualNode, out: &mut String) {
if let Some(text) = &node.text {
out.push_str(text);
out.push('\n');
}
for child in &node.children {
collect(child, out);
}
}
let mut out = String::new();
collect(&doc.to_virtual_dom(), &mut out);
out
}
mod css;
use css::*;
mod virtual_dom;
pub(crate) use virtual_dom::ToVirtualDom;
mod xpath;
pub(crate) use xpath::query_xpath;
#[cfg(test)]
mod tests {
mod xpath {
use super::super::*;
use crate::tests::prelude::*;
#[test]
fn assert_xpath_success() {
let doc = Parser::default().parse("* item 1\n* item 2\n* item 3");
assert_xpath(&doc, "//ul", 1);
assert_xpath(&doc, "//li", 3);
}
#[test]
#[should_panic(expected = "XPath query '//ul' expected 2 matches, found 1")]
fn assert_xpath_failure() {
let doc = Parser::default().parse("* item");
assert_xpath(&doc, "//ul", 2); }
#[test]
fn assert_xpath_with_path() {
let doc = Parser::default().parse("== Section\n\n* item 1\n* item 2");
assert_xpath(&doc, "//div/ul/li", 2);
}
#[test]
fn assert_xpath_with_predicate() {
let doc = Parser::default().parse("Hello\n\nWorld");
assert_xpath(&doc, "//p[text()=\"Hello\"]", 1);
}
#[test]
fn assert_xpath_with_single_quoted_text_and_newline() {
let doc =
Parser::default().parse("List\n====\n\n- Foo\nwrapped content\n- Boo\n- Blech\n");
assert_xpath(&doc, "//ul", 1);
assert_xpath(&doc, "//ul/li[1]/*", 1);
assert_xpath(&doc, "//ul/li[1]/p[text() = \'Foo\\nwrapped content\']", 1);
}
}
mod css {
use super::super::*;
use crate::tests::prelude::*;
#[test]
fn assert_css_success() {
let doc = Parser::default().parse("* item");
assert_css(&doc, "ul", 1);
}
#[test]
fn assert_css_with_class_selector() {
let doc = Parser::default().parse("* item");
assert_css(&doc, ".ulist", 1);
}
}
mod refute_output_contains {
use super::super::*;
use crate::tests::prelude::*;
#[test]
fn succeeds_when_no_such_content() {
let doc = Parser::default().parse("* item 1\n* item 2\n* item 3");
refute_output_contains(&doc, "blah");
}
#[test]
#[should_panic]
fn panics_when_content_found_directly() {
let doc = Parser::default().parse("On the wolpertanger hunt");
refute_output_contains(&doc, "wolpertanger");
}
#[test]
#[should_panic]
fn panics_when_content_found_nested() {
let doc = Parser::default().parse("* item 1\n** item 2\n* item 3");
refute_output_contains(&doc, "item 2");
}
}
}