144 lines
5.3 KiB
Rust
144 lines
5.3 KiB
Rust
//! Tests for search support.
|
|
|
|
use crate::prelude::*;
|
|
use mdbook_core::book::{BookItem, Chapter};
|
|
use snapbox::file;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
fn read_book_index(root: &Path) -> serde_json::Value {
|
|
let index = root.join("book/searchindex.js");
|
|
let index = std::fs::read_to_string(index).unwrap();
|
|
let index =
|
|
index.trim_start_matches("window.search = Object.assign(window.search, JSON.parse('");
|
|
let index = index.trim_end_matches("'));");
|
|
// We need unescape the string as it's supposed to be an escaped JS string.
|
|
serde_json::from_str(&index.replace("\\'", "'").replace("\\\\", "\\")).unwrap()
|
|
}
|
|
|
|
// Some spot checks for the generation of the search index.
|
|
#[test]
|
|
fn reasonable_search_index() {
|
|
let mut test = BookTest::from_dir("search/reasonable_search_index");
|
|
test.build();
|
|
let index = read_book_index(&test.dir);
|
|
|
|
let doc_urls = index["doc_urls"].as_array().unwrap();
|
|
eprintln!("doc_urls={doc_urls:#?}",);
|
|
let get_doc_ref = |url: &str| -> String {
|
|
doc_urls
|
|
.iter()
|
|
.position(|s| s == url)
|
|
.unwrap_or_else(|| panic!("failed to find {url}"))
|
|
.to_string()
|
|
};
|
|
|
|
let first_chapter = get_doc_ref("first/index.html#first-chapter");
|
|
let introduction = get_doc_ref("intro.html#introduction");
|
|
let some_section = get_doc_ref("first/index.html#some-section");
|
|
let summary = get_doc_ref("first/includes.html#summary");
|
|
let no_headers = get_doc_ref("first/no-headers.html");
|
|
let duplicate_headers_1 = get_doc_ref("first/duplicate-headers.html#header-text-1");
|
|
let heading_attrs = get_doc_ref("first/heading-attributes.html#both");
|
|
let sneaky = get_doc_ref("intro.html#sneaky");
|
|
|
|
let bodyidx = &index["index"]["index"]["body"]["root"];
|
|
let textidx = &bodyidx["t"]["e"]["x"]["t"];
|
|
assert_eq!(textidx["df"], 5);
|
|
assert_eq!(textidx["docs"][&first_chapter]["tf"], 1.0);
|
|
assert_eq!(textidx["docs"][&introduction]["tf"], 1.0);
|
|
|
|
let docs = &index["index"]["documentStore"]["docs"];
|
|
assert_eq!(docs[&first_chapter]["body"], "more text.");
|
|
assert_eq!(docs[&some_section]["body"], "");
|
|
assert_eq!(
|
|
docs[&summary]["body"],
|
|
"Introduction First Chapter Includes Unicode No Headers Duplicate Headers Heading Attributes"
|
|
);
|
|
assert_eq!(
|
|
docs[&summary]["breadcrumbs"],
|
|
"First Chapter » Includes » Summary"
|
|
);
|
|
// See note about InlineHtml in search.rs. Ideally the `alert()` part
|
|
// should not be in the index, but we don't have a way to scrub inline
|
|
// html.
|
|
assert_eq!(
|
|
docs[&sneaky]["body"],
|
|
"I put <HTML> in here! Sneaky inline event alert(\"inline\");. But regular inline is indexed."
|
|
);
|
|
assert_eq!(
|
|
docs[&no_headers]["breadcrumbs"],
|
|
"First Chapter » No Headers"
|
|
);
|
|
assert_eq!(
|
|
docs[&duplicate_headers_1]["breadcrumbs"],
|
|
"First Chapter » Duplicate Headers » Header Text"
|
|
);
|
|
assert_eq!(
|
|
docs[&no_headers]["body"],
|
|
"Capybara capybara capybara. Capybara capybara capybara. ThisLongWordIsIncludedSoWeCanCheckThatSufficientlyLongWordsAreOmittedFromTheSearchIndex."
|
|
);
|
|
assert_eq!(
|
|
docs[&heading_attrs]["breadcrumbs"],
|
|
"First Chapter » Heading Attributes » Heading with id and classes"
|
|
);
|
|
}
|
|
|
|
// This test is here to catch any unexpected changes to the search index.
|
|
#[test]
|
|
fn search_index_hasnt_changed_accidentally() {
|
|
BookTest::from_dir("search/reasonable_search_index").check_file(
|
|
"book/searchindex.js",
|
|
file!["search/reasonable_search_index/expected_index.js"],
|
|
);
|
|
}
|
|
|
|
// Ability to disable search chapters.
|
|
#[test]
|
|
fn can_disable_individual_chapters() {
|
|
let mut test = BookTest::from_dir("search/disable_search_chapter");
|
|
test.build();
|
|
let index = read_book_index(&test.dir);
|
|
let doc_urls = index["doc_urls"].as_array().unwrap();
|
|
let contains = |path| {
|
|
doc_urls
|
|
.iter()
|
|
.any(|p| p.as_str().unwrap().starts_with(path))
|
|
};
|
|
assert!(contains("second.html"));
|
|
assert!(!contains("second/"));
|
|
assert!(!contains("first/disable_me.html"));
|
|
assert!(contains("first/keep_me.html"));
|
|
}
|
|
|
|
// Test for a regression where search would fail if source_path is None.
|
|
// https://github.com/rust-lang/mdBook/pull/2550
|
|
#[test]
|
|
fn with_no_source_path() {
|
|
let test = BookTest::from_dir("search/reasonable_search_index");
|
|
let mut book = test.load_book();
|
|
let chapter = Chapter {
|
|
name: "Sample chapter".to_string(),
|
|
content: "".to_string(),
|
|
number: None,
|
|
sub_items: Vec::new(),
|
|
path: Some(PathBuf::from("sample.html")),
|
|
source_path: None,
|
|
parent_names: Vec::new(),
|
|
};
|
|
book.book.sections.push(BookItem::Chapter(chapter));
|
|
book.build().unwrap();
|
|
}
|
|
|
|
// Checks that invalid settings in search chapter is rejected.
|
|
#[test]
|
|
fn chapter_settings_validation_error() {
|
|
BookTest::from_dir("search/chapter_settings_validation_error").run("build", |cmd| {
|
|
cmd.expect_failure().expect_stderr(str![[r#"
|
|
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
|
|
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
|
|
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Rendering failed
|
|
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: [output.html.search.chapter] key `does-not-exist` does not match any chapter paths
|
|
|
|
"#]]);
|
|
});
|
|
}
|