Skip to content

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

Merged
merged 9 commits into from
Aug 2, 2017
9 changes: 9 additions & 0 deletions tests/dummy-book/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Summary

[Introduction](intro.md)

- [First Chapter](./first/index.md)
- [Nested Chapter](./first/nested.md)
- [Second Chapter](./second.md)

[Conclusion](./conclusion.md)
1 change: 1 addition & 0 deletions tests/dummy-book/conclusion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Conclusion
3 changes: 3 additions & 0 deletions tests/dummy-book/first/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# First Chapter

more text.
7 changes: 7 additions & 0 deletions tests/dummy-book/first/nested.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Nested Chapter

This file has some testable code.

```rust
assert!($TEST_STATUS);
```
3 changes: 3 additions & 0 deletions tests/dummy-book/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Introduction

Here's some interesting text...
1 change: 1 addition & 0 deletions tests/dummy-book/second.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Second Chapter
110 changes: 110 additions & 0 deletions tests/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! 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 contents from the `tests/dummy-book/` directory.


#![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 = include_str!("dummy-book/SUMMARY.md");
const INTRO: &'static str = include_str!("dummy-book/intro.md");
const FIRST: &'static str = include_str!("dummy-book/first/index.md");
const NESTED: &'static str = include_str!("dummy-book/first/nested.md");
const SECOND: &'static str = include_str!("dummy-book/second.md");
const CONCLUSION: &'static str = include_str!("dummy-book/conclusion.md");


/// 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.
#[derive(Clone, Debug, PartialEq)]
pub struct DummyBook {
passing_test: bool,
}

impl DummyBook {
/// Create a new `DummyBook` with all the defaults.
pub fn new() -> DummyBook {
DummyBook::default()
}

/// Whether the doc-test included in the "Nested Chapter" should pass or
/// fail (it passes by default).
pub fn with_passing_test(&mut self, test_passes: bool) -> &mut Self {
self.passing_test = test_passes;
self
}

/// Write a book to a temporary directory using the provided settings.
///
/// # Note
///
/// If this fails for any reason it will `panic!()`. If we can't write to a
/// temporary directory then chances are you've got bigger problems...
pub fn build(&self) -> TempDir {
let temp = TempDir::new("dummy_book").unwrap();

let src = temp.path().join("src");
fs::create_dir_all(&src).unwrap();

let first = src.join("first");
fs::create_dir_all(&first).unwrap();

let to_substitute = if self.passing_test { "true" } else { "false" };
let nested_text = NESTED.replace("$TEST_STATUS", to_substitute);

let inputs = vec![
(src.join("SUMMARY.md"), SUMMARY_MD),
(src.join("intro.md"), INTRO),
(first.join("index.md"), FIRST),
(first.join("nested.md"), &nested_text),
(src.join("second.md"), SECOND),
(src.join("conclusion.md"), CONCLUSION),
];

for (path, content) in inputs {
File::create(path)
.unwrap()
.write_all(content.as_bytes())
.unwrap();
}

temp
}
}

impl Default for DummyBook {
fn default() -> DummyBook {
DummyBook { passing_test: true }
}
}


/// 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);
}
}
47 changes: 47 additions & 0 deletions tests/init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
extern crate mdbook;
extern crate tempdir;

use tempdir::TempDir;
use mdbook::MDBook;


/// Run `mdbook init` in an empty directory and make sure the default files
/// are created.
#[test]
fn base_mdbook_init_should_create_default_content() {
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);
}
}

/// Set some custom arguments for where to place the source and destination
/// files, then call `mdbook init`.
#[test]
fn run_mdbook_init_with_custom_book_and_src_locations() {
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(), "{} shouldn't exist yet!", file);
}

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(), "{} should have been created by `mdbook init`", file);
}
}
109 changes: 109 additions & 0 deletions tests/rendered_output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
extern crate mdbook;
extern crate tempdir;

mod helpers;
use mdbook::MDBook;


/// Make sure you can load the dummy book and build it without panicking.
#[test]
fn build_the_dummy_book() {
let temp = helpers::DummyBook::default().build();
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::DummyBook::default().build();
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::DummyBook::default().build();
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::DummyBook::default().build();
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::DummyBook::default().build();
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::DummyBook::default().build();
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]);
}
}
26 changes: 26 additions & 0 deletions tests/testing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
extern crate tempdir;
extern crate mdbook;

mod helpers;
use mdbook::MDBook;


#[test]
fn mdbook_can_correctly_test_a_passing_book() {
let temp = helpers::DummyBook::default()
.with_passing_test(true)
.build();
let mut md = MDBook::new(temp.path());

assert!(md.test(vec![]).is_ok());
}

#[test]
fn mdbook_detects_book_with_failing_tests() {
let temp = helpers::DummyBook::default()
.with_passing_test(false)
.build();
let mut md: MDBook = MDBook::new(temp.path());

assert!(md.test(vec![]).is_err());
}