"##
]],
);
}
// Fontawesome `` tag support.
#[test]
fn fontawesome() {
BookTest::from_dir("rendering/fontawesome")
.run("build", |cmd| {
cmd.expect_stderr(str![[r#"
INFO Book building has started
INFO Running the html backend
WARN failed to find Font Awesome icon for icon `does-not-exist` with type `regular` in `fa.md`: Invalid Font Awesome icon name: visit https://fontawesome.com/icons?d=gallery&m=free to see valid names
INFO HTML book written to `[ROOT]/book`
"#]]);
})
.check_all_main_files();
}
// Verifies that an invalid `git-repository-icon` in book.toml produces a
// helpful error message with the icon name, type, and a link to FontAwesome.
#[test]
fn fontawesome_error_message() {
BookTest::from_dir("rendering/fontawesome_error")
.run("build", |cmd| {
cmd.expect_failure();
cmd.expect_stderr(str![[r#"
INFO Book building has started
INFO Running the html backend
ERROR Rendering failed
[TAB]Caused by: Error rendering "index" line [..], col [..]: Unknown Font Awesome icon `github` for type `regular`. Hint: check the icon name and prefix (fas (solid), fab (brands), or far (regular)) at https://fontawesome.com/v6/search?m=free
[TAB]Caused by: Unknown Font Awesome icon `github` for type `regular`. Hint: check the icon name and prefix (fas (solid), fab (brands), or far (regular)) at https://fontawesome.com/v6/search?m=free
"#]]);
});
}
// Tests the rendering when setting the default rust edition.
#[test]
fn default_rust_edition() {
BookTest::from_dir("rendering/default_rust_edition").check_all_main_files();
}
// Tests the rendering for editable code blocks.
#[test]
fn editable_rust_block() {
BookTest::from_dir("rendering/editable_rust_block").check_all_main_files();
}
// Tests for custom hide lines.
#[test]
fn hidelines() {
BookTest::from_dir("rendering/hidelines").check_all_main_files();
}
// Tests for code blocks of basic rust code.
#[test]
fn language_rust_playground() {
fn expect(input: &str, info: &str, expected: impl snapbox::IntoData) {
BookTest::init(|_| {})
.change_file("book.toml", "output.html.playground.editable = true")
.change_file("src/chapter_1.md", &format!("```rust {info}\n{input}\n```"))
.check_main_file("book/chapter_1.html", expected);
}
// No-main should be wrapped in `fn main` boring lines.
expect(
"x()",
"",
str![[r#"
#![allow(unused)]
fn main() {
x()
}
"#]],
);
// `fn main` should not be wrapped, not boring.
expect(
"fn main() {}",
"",
str![[r#"
fn main() {}
"#]],
);
// Lines starting with `#` are boring.
expect(
"let s = \"foo\n # bar\n\";",
"editable",
str![[r#"
let s = "foo
bar
";
"#]],
);
// `##` is not boring and is used as an escape.
expect(
"let s = \"foo\n ## bar\n\";",
"editable",
str![[r#"
let s = "foo
# bar
";
"#]],
);
// `#` on a line by itself is boring.
expect(
"let s = \"foo\n # bar\n#\n\";",
"editable",
str![[r#"
let s = "foo
bar
";
"#]],
);
// `#` must be followed by a space to be boring.
expect(
"#x;",
"",
str![[r#"
#![allow(unused)]
fn main() {
#x;
}
"#]],
);
// Other classes like "ignore" should not change things, and the class is
// included in the code tag.
expect(
"let s = \"foo\n # bar\n\";",
"ignore",
str![[r#"
let s = "foo
bar
";
"#]],
);
// Inner attributes and normal attributes are not boring.
expect(
"#![no_std]\nlet s = \"foo\";\n #[some_attr]",
"editable",
str![[r#"
#![no_std]
let s = "foo";
#[some_attr]
"#]],
);
}
// Rust code block in a list.
#[test]
fn code_block_in_list() {
BookTest::init(|_| {})
.change_file(
"src/chapter_1.md",
r#"- inside list
```rust
fn foo() {
let x = 1;
}
```
"#,
)
.check_main_file(
"book/chapter_1.html",
str![[r#"
inside list
#![allow(unused)]
fn main() {
fn foo() {
let x = 1;
}
}
"#]],
);
}
// Checks the rendering of links added to headers.
#[test]
fn header_links() {
BookTest::from_dir("rendering/header_links").check_all_main_files();
}
// A corrupted HTML end tag.
#[test]
fn busted_end_tag() {
BookTest::init(|_| {})
.change_file("src/chapter_1.md", "
xfooy
")
.run("build", |cmd| {
cmd.expect_stderr(str![[r#"
INFO Book building has started
INFO Running the html backend
WARN html parse error in `chapter_1.md`: Self-closing end tag
Html text was:
xfooy
INFO HTML book written to `[ROOT]/book`
"#]]);
})
.check_main_file("book/chapter_1.html", str!["
xfooy
"]);
}
// Various html blocks.
#[test]
fn html_blocks() {
BookTest::from_dir("rendering/html_blocks").check_all_main_files();
}
// Test for a fenced code block that is also indented.
#[test]
fn code_block_fenced_with_indent() {
BookTest::from_dir("rendering/code_blocks_fenced_with_indent").check_all_main_files();
}
// Unclosed HTML tags.
//
// Note that the HTML parsing algorithm is much more complicated than what
// this is checking.
#[test]
fn unclosed_html_tags() {
BookTest::init(|_| {})
.change_file("src/chapter_1.md", "
xfooxyz")
.run("build", |cmd| {
cmd.expect_stderr(str![[r#"
INFO Book building has started
INFO Running the html backend
WARN unclosed HTML tag `` found in `chapter_1.md`
WARN unclosed HTML tag `` found in `chapter_1.md`
WARN unclosed HTML tag `
` found in `chapter_1.md`
INFO HTML book written to `[ROOT]/book`
"#]]);
})
.check_main_file(
"book/chapter_1.html",
str!["
xfooxyz
"],
);
}
// Test for HTML tags out of sync.
#[test]
fn unbalanced_html_tags() {
BookTest::init(|_| {})
.change_file("src/chapter_1.md", "
xfoo
")
.run("build", |cmd| {
cmd.expect_stderr(str![[r#"
INFO Book building has started
INFO Running the html backend
WARN unexpected HTML end tag `
` found in `chapter_1.md`
Check that the HTML tags are properly balanced.
WARN unclosed HTML tag `
` found in `chapter_1.md`
INFO HTML book written to `[ROOT]/book`
"#]]);
})
.check_main_file("book/chapter_1.html", str!["
xfoo
"]);
}
// Test for bug with unbalanced HTML handling in the heading.
#[test]
fn heading_with_unbalanced_html() {
BookTest::init(|_| {})
.change_file("src/chapter_1.md", "### Option")
.run("build", |cmd| {
cmd.expect_stderr(str![[r#"
INFO Book building has started
INFO Running the html backend
WARN unclosed HTML tag `` found in `chapter_1.md` while exiting Heading(H3)
HTML tags must be closed before exiting a markdown element.
INFO HTML book written to `[ROOT]/book`
"#]]);
})
.check_main_file(
"book/chapter_1.html",
str![[r##"
"##]],
);
}
// The following tests cover the `output.html.site-url` feature, which makes
// every generated link absolute (rooted at `site-url`) so a book served from a
// subdirectory resolves cross-chapter, asset, and sidebar links regardless of
// the page's own depth. See https://github.com/rust-lang/mdBook/pull/1802.
// Root-relative `./` links written in chapter content are anchored to the site
// URL, while links with a scheme (e.g. `https`) are left untouched.
#[test]
fn site_url_rewrites_content_links() {
BookTest::from_dir("rendering/site_url")
.check_file_contains(
"book/nested/deep.html",
"other chapter",
)
.check_file_contains(
"book/index.html",
"deep chapter",
)
.check_file_contains(
"book/index.html",
"external link",
);
}
// `path_to_root` (used by the page chrome, prev/next navigation, and the
// JavaScript sidebar in `toc.js`) becomes the absolute site URL on every page,
// independent of how deeply the page is nested.
#[test]
fn site_url_sets_absolute_path_to_root() {
BookTest::from_dir("rendering/site_url").check_file_contains(
"book/nested/deep.html",
"const path_to_root = \"https://example.com/docs/\";",
);
}
// Static assets resolved through the `{{resource}}` helper are emitted with the
// absolute site URL rather than a depth-relative `../` prefix.
#[test]
fn site_url_makes_assets_absolute() {
BookTest::from_dir("rendering/site_url").check_file_contains(
"book/nested/deep.html",
"` of the site URL so its root-relative chapter links resolve
// absolutely.
#[test]
fn site_url_sets_toc_html_base() {
BookTest::from_dir("rendering/site_url")
.check_file_contains("book/toc.html", "");
}
// The `` from `toc.html` must not leak onto regular chapter pages,
// which would break their page-relative content links.
#[test]
fn site_url_no_base_href_on_chapter_pages() {
BookTest::from_dir("rendering/site_url")
.check_file_doesnt_contain("book/nested/deep.html", "`
// is emitted: the feature is strictly opt-in.
#[test]
fn site_url_absent_keeps_links_relative() {
BookTest::init(|_| {})
.check_file_contains("book/index.html", "const path_to_root = \"\";")
.check_file_doesnt_contain("book/index.html", "deep chapter")
.check_file_contains("book/print.html", "other chapter")
.check_file_contains(
"book/print.html",
"external link",
);
}