-
Notifications
You must be signed in to change notification settings - Fork 1.9k
High level integration tests #374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
9fab267
e3f047a
8b7c95e
cd11035
c90c0f7
7e8819f
29e00c0
0c3a2b8
e2eb40b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| //! Helpers for tests which exercise the overall application, in particular | ||
| //! the `MDBook` initialization and build/rendering process. | ||
| //! | ||
| //! This will create an entire book in a temporary directory using some | ||
| //! dummy content. | ||
|
|
||
| #![allow(dead_code, unused_variables, unused_imports)] | ||
| extern crate tempdir; | ||
|
|
||
| use std::path::Path; | ||
| use std::fs::{self, File}; | ||
| use std::io::{Read, Write}; | ||
|
|
||
| use tempdir::TempDir; | ||
|
|
||
|
|
||
| const SUMMARY_MD: &'static str = "# Summary | ||
|
|
||
| [Introduction](intro.md) | ||
|
|
||
| - [First Chapter](./first/index.md) | ||
| - [Nested Chapter](./first/nested.md) | ||
| - [Second Chapter](./second.md) | ||
|
|
||
| [Conclusion](./conclusion.md) | ||
| "; | ||
|
|
||
| const INTRO: &'static str = "# Introduction | ||
|
|
||
| Here's some interesting text..."; | ||
|
|
||
| const FIRST: &'static str = "# First Chapter | ||
|
|
||
| more text."; | ||
|
|
||
| const NESTED: &'static str = r#"# Nested Chapter | ||
|
|
||
| This file has some testable code. | ||
|
|
||
| ```rust | ||
| assert!($TEST_STATUS); | ||
| ```"#; | ||
|
|
||
| const SECOND: &'static str = "# Second Chapter"; | ||
|
|
||
| const CONCLUSION: &'static str = "# Conclusion"; | ||
|
|
||
|
|
||
| /// Create a dummy book in a temporary directory, using the contents of | ||
| /// `SUMMARY_MD` as a guide. | ||
| /// | ||
| /// The "Nested Chapter" file contains a code block with a single | ||
| /// `assert!($TEST_STATUS)`. If you want to check MDBook's testing | ||
| /// functionality, `$TEST_STATUS` can be substitute for either `true` or | ||
| /// `false`. This is done using the `passing_test` parameter. | ||
| pub fn create_book(passing_test: bool) -> TempDir { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a little worried that we will end up with a huge number of arguments for this function in the future.. Currently there is only one argument Maybe we should make this a builder struct with a good set of default options? In the simplest case we will have to call
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the |
||
| let temp = TempDir::new("dummy_book").unwrap(); | ||
|
|
||
| let src = temp.path().join("src"); | ||
| fs::create_dir_all(&src).unwrap(); | ||
|
|
||
| File::create(src.join("SUMMARY.md")) | ||
| .unwrap() | ||
| .write_all(SUMMARY_MD.as_bytes()) | ||
| .unwrap(); | ||
| File::create(src.join("intro.md")) | ||
| .unwrap() | ||
| .write_all(INTRO.as_bytes()) | ||
| .unwrap(); | ||
|
|
||
| let first = src.join("first"); | ||
| fs::create_dir_all(&first).unwrap(); | ||
| File::create(first.join("index.md")) | ||
| .unwrap() | ||
| .write_all(FIRST.as_bytes()) | ||
| .unwrap(); | ||
|
|
||
| let to_substitute = if passing_test { "true" } else { "false" }; | ||
| let nested_text = NESTED.replace("$TEST_STATUS", to_substitute); | ||
| File::create(first.join("nested.md")) | ||
| .unwrap() | ||
| .write_all(nested_text.as_bytes()) | ||
| .unwrap(); | ||
|
|
||
| File::create(src.join("second.md")) | ||
| .unwrap() | ||
| .write_all(SECOND.as_bytes()) | ||
| .unwrap(); | ||
| File::create(src.join("conclusion.md")) | ||
| .unwrap() | ||
| .write_all(CONCLUSION.as_bytes()) | ||
| .unwrap(); | ||
|
|
||
| temp | ||
| } | ||
|
|
||
| /// Read the contents of the provided file into memory and then iterate through | ||
| /// the list of strings asserting that the file contains all of them. | ||
| pub fn assert_contains_strings<P: AsRef<Path>>(filename: P, strings: &[&str]) { | ||
| let filename = filename.as_ref(); | ||
|
|
||
| let mut content = String::new(); | ||
| File::open(&filename) | ||
| .expect("Couldn't open the provided file") | ||
| .read_to_string(&mut content) | ||
| .expect("Couldn't read the file's contents"); | ||
|
|
||
| for s in strings { | ||
| assert!(content.contains(s), "Searching for {:?} in {}\n\n{}", s, filename.display(), content); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| extern crate mdbook; | ||
| extern crate tempdir; | ||
|
|
||
| use tempdir::TempDir; | ||
| use mdbook::MDBook; | ||
|
|
||
|
|
||
| #[test] | ||
| fn run_mdbook_init() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe you could add one or two lines of comments to explain what each test does?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. |
||
| let created_files = vec!["book", "src", "src/SUMMARY.md", "src/chapter_1.md"]; | ||
|
|
||
| let temp = TempDir::new("mdbook").unwrap(); | ||
| for file in &created_files { | ||
| assert!(!temp.path().join(file).exists()); | ||
| } | ||
|
|
||
| let mut md = MDBook::new(temp.path()); | ||
| md.init().unwrap(); | ||
|
|
||
| for file in &created_files { | ||
| assert!(temp.path().join(file).exists(), "{} doesn't exist", file); | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn run_mdbook_init_with_custom_args() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe the name can reflect that it specifically tests the source and output destination? |
||
| let created_files = vec!["out", "in", "in/SUMMARY.md", "in/chapter_1.md"]; | ||
|
|
||
| let temp = TempDir::new("mdbook").unwrap(); | ||
| for file in &created_files { | ||
| assert!(!temp.path().join(file).exists()); | ||
| } | ||
|
|
||
| let mut md = MDBook::new(temp.path()) | ||
| .with_source("in") | ||
| .with_destination("out"); | ||
|
|
||
| md.init().unwrap(); | ||
|
|
||
| for file in &created_files { | ||
| assert!(temp.path().join(file).exists(), "{} doesn't exist", file); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| extern crate mdbook; | ||
| extern crate tempdir; | ||
|
|
||
| mod helpers; | ||
| use mdbook::MDBook; | ||
|
|
||
|
|
||
| #[test] | ||
| fn build_the_dummy_book() { | ||
| let temp = helpers::create_book(true); | ||
| let mut md = MDBook::new(temp.path()); | ||
|
|
||
| md.build().unwrap(); | ||
| } | ||
|
|
||
| #[test] | ||
| fn by_default_mdbook_generates_rendered_content_in_the_book_directory() { | ||
| let temp = helpers::create_book(false); | ||
| let mut md = MDBook::new(temp.path()); | ||
|
|
||
| assert!(!temp.path().join("book").exists()); | ||
| md.build().unwrap(); | ||
|
|
||
| assert!(temp.path().join("book").exists()); | ||
| assert!(temp.path().join("book").join("index.html").exists()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn make_sure_bottom_level_files_contain_links_to_chapters() { | ||
| let temp = helpers::create_book(false); | ||
| let mut md = MDBook::new(temp.path()); | ||
| md.build().unwrap(); | ||
|
|
||
| let dest = temp.path().join("book"); | ||
| let links = vec![ | ||
| "intro.html", | ||
| "first/index.html", | ||
| "first/nested.html", | ||
| "second.html", | ||
| "conclusion.html", | ||
| ]; | ||
|
|
||
| let files_in_bottom_dir = vec!["index.html", "intro.html", "second.html", "conclusion.html"]; | ||
|
|
||
| for filename in files_in_bottom_dir { | ||
| helpers::assert_contains_strings(dest.join(filename), &links); | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn check_correct_cross_links_in_nested_dir() { | ||
| let temp = helpers::create_book(false); | ||
| let mut md = MDBook::new(temp.path()); | ||
| md.build().unwrap(); | ||
|
|
||
| let first = temp.path().join("book").join("first"); | ||
| let links = vec![ | ||
| r#"<base href="../">"#, | ||
| "intro.html", | ||
| "first/index.html", | ||
| "first/nested.html", | ||
| "second.html", | ||
| "conclusion.html", | ||
| ]; | ||
|
|
||
| let files_in_nested_dir = vec!["index.html", "nested.html"]; | ||
|
|
||
| for filename in files_in_nested_dir { | ||
| helpers::assert_contains_strings(first.join(filename), &links); | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn rendered_code_has_playpen_stuff() { | ||
| let temp = helpers::create_book(true); | ||
| let mut md = MDBook::new(temp.path()); | ||
| md.build().unwrap(); | ||
|
|
||
| let nested = temp.path().join("book/first/nested.html"); | ||
| let playpen_class = vec![r#"class="playpen""#]; | ||
|
|
||
| helpers::assert_contains_strings(nested, &playpen_class); | ||
|
|
||
| let book_js = temp.path().join("book/book.js"); | ||
| helpers::assert_contains_strings(book_js, &[".playpen"]); | ||
| } | ||
|
|
||
| #[test] | ||
| fn chapter_content_appears_in_rendered_document() { | ||
| let content = vec![ | ||
| ("index.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 = helpers::create_book(true); | ||
| let mut md = MDBook::new(temp.path()); | ||
| md.build().unwrap(); | ||
|
|
||
| let destination = temp.path().join("book"); | ||
|
|
||
| for (filename, text) in content { | ||
| let path = destination.join(filename); | ||
| helpers::assert_contains_strings(path, &[text]); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| extern crate tempdir; | ||
| extern crate mdbook; | ||
|
|
||
| mod helpers; | ||
| use mdbook::MDBook; | ||
|
|
||
|
|
||
| #[test] | ||
| fn mdbook_can_correctly_test_a_passing_book() { | ||
| let temp = helpers::create_book(true); | ||
| let mut md = MDBook::new(temp.path()); | ||
|
|
||
| assert!(md.test(vec![]).is_ok()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn mdbook_detects_book_with_failing_tests() { | ||
| let temp = helpers::create_book(false); | ||
| let mut md: MDBook = MDBook::new(temp.path()); | ||
|
|
||
| assert!(md.test(vec![]).is_err()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an idea for maintainability:
We could make a simple directory with a dummy book like:
and then include the files like this
It makes it easier to edit and expand the book when we add more tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, since you convert to bytes later you can directly use
ìnclude_bytes!()