#[macro_use] extern crate pretty_assertions; mod dummy_book; use crate::dummy_book::{assert_contains_strings, assert_doesnt_contain_strings, DummyBook}; use anyhow::Context; use mdbook::config::Config; use mdbook::errors::*; use mdbook::utils::fs::write_file; use mdbook::MDBook; use select::document::Document; use select::predicate::{Class, Name, Predicate}; use std::collections::HashMap; use std::ffi::OsStr; use std::fs; use std::io::Write; use std::path::{Component, Path, PathBuf}; use tempfile::Builder as TempFileBuilder; use walkdir::{DirEntry, WalkDir}; const BOOK_ROOT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/dummy_book"); const TOC_TOP_LEVEL: &[&str] = &[ "1. First Chapter", "2. Second Chapter", "Conclusion", "Dummy Book", "Introduction", ]; const TOC_SECOND_LEVEL: &[&str] = &[ "1.1. Nested Chapter", "1.2. Includes", "1.3. Recursive", "1.4. Markdown", "1.5. Unicode", "2.1. Nested Chapter", ]; /// Make sure you can load the dummy book and build it without panicking. #[test] fn build_the_dummy_book() { let temp = DummyBook::new().build().unwrap(); let md = MDBook::load(temp.path()).unwrap(); md.build().unwrap(); } #[test] fn by_default_mdbook_generates_rendered_content_in_the_book_directory() { let temp = DummyBook::new().build().unwrap(); let md = MDBook::load(temp.path()).unwrap(); assert!(!temp.path().join("book").exists()); md.build().unwrap(); assert!(temp.path().join("book").exists()); let index_file = md.build_dir_for("html").join("index.html"); assert!(index_file.exists()); } #[test] fn make_sure_bottom_level_files_contain_links_to_chapters() { let temp = DummyBook::new().build().unwrap(); let md = MDBook::load(temp.path()).unwrap(); md.build().unwrap(); let dest = temp.path().join("book"); let links = vec![ r#"href="intro.html""#, r#"href="first/index.html""#, r#"href="first/nested.html""#, r#"href="second.html""#, r#"href="conclusion.html""#, ]; let files_in_bottom_dir = vec!["index.html", "intro.html", "second.html", "conclusion.html"]; for filename in files_in_bottom_dir { assert_contains_strings(dest.join(filename), &links); } } #[test] fn check_correct_cross_links_in_nested_dir() { let temp = DummyBook::new().build().unwrap(); let md = MDBook::load(temp.path()).unwrap(); md.build().unwrap(); let first = temp.path().join("book").join("first"); let links = vec![ r#"href="../intro.html""#, r#"href="../first/index.html""#, r#"href="../first/nested.html""#, r#"href="../second.html""#, r#"href="../conclusion.html""#, ]; let files_in_nested_dir = vec!["index.html", "nested.html"]; for filename in files_in_nested_dir { assert_contains_strings(first.join(filename), &links); } assert_contains_strings( first.join("index.html"), &[r##"
"##,
r##"fragment link"##,
r##"HTML Link"##,
r##"
"##,
],
);
}
#[test]
fn rendered_code_has_playground_stuff() {
let temp = DummyBook::new().build().unwrap();
let md = MDBook::load(temp.path()).unwrap();
md.build().unwrap();
let nested = temp.path().join("book/first/nested.html");
let playground_class = vec![r#"class="playground""#];
assert_contains_strings(nested, &playground_class);
let book_js = temp.path().join("book/book.js");
assert_contains_strings(book_js, &[".playground"]);
}
#[test]
fn anchors_include_text_between_but_not_anchor_comments() {
let temp = DummyBook::new().build().unwrap();
let md = MDBook::load(temp.path()).unwrap();
md.build().unwrap();
let nested = temp.path().join("book/first/nested.html");
let text_between_anchors = vec!["unique-string-for-anchor-test"];
let anchor_text = vec!["ANCHOR"];
assert_contains_strings(nested.clone(), &text_between_anchors);
assert_doesnt_contain_strings(nested, &anchor_text);
}
#[test]
fn rustdoc_include_hides_the_unspecified_part_of_the_file() {
let temp = DummyBook::new().build().unwrap();
let md = MDBook::load(temp.path()).unwrap();
md.build().unwrap();
let nested = temp.path().join("book/first/nested.html");
let text = vec![
"fn some_function() {",
"fn some_other_function() {",
];
assert_contains_strings(nested, &text);
}
#[test]
fn chapter_content_appears_in_rendered_document() {
let content = vec![
("index.html", "This file is just here to cause the"),
("intro.html", "Here's some interesting text"),
("second.html", "Second Chapter"),
("first/nested.html", "testable code"),
("first/index.html", "more text"),
("conclusion.html", "Conclusion"),
];
let temp = DummyBook::new().build().unwrap();
let md = MDBook::load(temp.path()).unwrap();
md.build().unwrap();
let destination = temp.path().join("book");
for (filename, text) in content {
let path = destination.join(filename);
assert_contains_strings(path, &[text]);
}
}
/// Apply a series of predicates to some root predicate, where each
/// successive predicate is the descendant of the last one. Similar to how you
/// might do `ul.foo li a` in CSS to access all anchor tags in the `foo` list.
macro_rules! descendants {
($root:expr, $($child:expr),*) => {
$root
$(
.descendant($child)
)*
};
}
/// Make sure that all `*.md` files (excluding `SUMMARY.md`) were rendered
/// and placed in the `book` directory with their extensions set to `*.html`.
#[test]
fn chapter_files_were_rendered_to_html() {
let temp = DummyBook::new().build().unwrap();
let src = Path::new(BOOK_ROOT).join("src");
let chapter_files = WalkDir::new(&src)
.into_iter()
.filter_entry(|entry| entry_ends_with(entry, ".md"))
.filter_map(std::result::Result::ok)
.map(|entry| entry.path().to_path_buf())
.filter(|path| path.file_name().and_then(OsStr::to_str) != Some("SUMMARY.md"));
for chapter in chapter_files {
let rendered_location = temp
.path()
.join(chapter.strip_prefix(&src).unwrap())
.with_extension("html");
assert!(
rendered_location.exists(),
"{} doesn't exits",
rendered_location.display()
);
}
}
fn entry_ends_with(entry: &DirEntry, ending: &str) -> bool {
entry.file_name().to_string_lossy().ends_with(ending)
}
/// Read the main page (`book/index.html`) and expose it as a DOM which we
/// can search with the `select` crate
fn root_index_html() -> Result