diff --git a/src/config.rs b/src/config.rs index 5c23a0c..308f6dc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -87,7 +87,9 @@ impl Config { /// Makes a url, taking into account that the base url might have a trailing slash pub fn make_permalink(&self, path: &str) -> String { - if self.base_url.ends_with('/') { + if self.base_url.ends_with('/') && path.starts_with('/') { + format!("{}{}", self.base_url, &path[1..]) + } else if self.base_url.ends_with('/') { format!("{}{}", self.base_url, path) } else { format!("{}/{}", self.base_url, path) @@ -95,8 +97,9 @@ impl Config { } } +/// Exists only for testing purposes +#[doc(hidden)] impl Default for Config { - /// Exists for testing purposes fn default() -> Config { Config { title: "".to_string(), @@ -181,4 +184,17 @@ hello = "world" assert_eq!(config.unwrap().extra.unwrap().get("hello").unwrap().as_str().unwrap(), "world"); } + #[test] + fn can_make_url_with_non_trailing_slash_base_url() { + let mut config = Config::default(); + config.base_url = "http://vincent.is".to_string(); + assert_eq!(config.make_permalink("hello"), "http://vincent.is/hello"); + } + + #[test] + fn can_make_url_with_trailing_slash_path() { + let mut config = Config::default(); + config.base_url = "http://vincent.is/".to_string(); + assert_eq!(config.make_permalink("/hello"), "http://vincent.is/hello"); + } } diff --git a/src/content/page.rs b/src/content/page.rs index ee6faf0..4062374 100644 --- a/src/content/page.rs +++ b/src/content/page.rs @@ -226,3 +226,144 @@ impl ser::Serialize for Page { state.end() } } + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use std::fs::{File, create_dir}; + use std::path::Path; + + use tera::Tera; + use tempdir::TempDir; + + use config::Config; + use super::Page; + + + #[test] + fn test_can_parse_a_valid_page() { + let content = r#" ++++ +title = "Hello" +description = "hey there" +slug = "hello-world" ++++ +Hello world"#; + let res = Page::parse(Path::new("post.md"), content, &Config::default()); + assert!(res.is_ok()); + let mut page = res.unwrap(); + page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); + + assert_eq!(page.meta.title.unwrap(), "Hello".to_string()); + assert_eq!(page.meta.slug.unwrap(), "hello-world".to_string()); + assert_eq!(page.raw_content, "Hello world".to_string()); + assert_eq!(page.content, "
Hello world
\n".to_string()); + } + + #[test] + fn test_can_make_url_from_sections_and_slug() { + let content = r#" + +++ + slug = "hello-world" + +++ + Hello world"#; + let mut conf = Config::default(); + conf.base_url = "http://hello.com/".to_string(); + let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &conf); + assert!(res.is_ok()); + let mut page = res.unwrap(); + page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); + assert_eq!(page.path, "posts/intro/hello-world"); + assert_eq!(page.permalink, "http://hello.com/posts/intro/hello-world"); + } + + #[test] + fn can_make_url_from_slug_only() { + let content = r#" + +++ + slug = "hello-world" + +++ + Hello world"#; + let config = Config::default(); + let res = Page::parse(Path::new("start.md"), content, &config); + assert!(res.is_ok()); + let mut page = res.unwrap(); + page.render_markdown(&HashMap::default(), &Tera::default(), &config).unwrap(); + assert_eq!(page.path, "hello-world"); + assert_eq!(page.permalink, config.make_permalink("hello-world")); + } + + #[test] + fn errors_on_invalid_front_matter_format() { + // missing starting +++ + let content = r#" + title = "Hello" + description = "hey there" + slug = "hello-world" + +++ + Hello world"#; + let res = Page::parse(Path::new("start.md"), content, &Config::default()); + assert!(res.is_err()); + } + + #[test] + fn can_make_slug_from_non_slug_filename() { + let config = Config::default(); + let res = Page::parse(Path::new(" file with space.md"), "+++\n+++", &config); + assert!(res.is_ok()); + let mut page = res.unwrap(); + page.render_markdown(&HashMap::default(), &Tera::default(), &config).unwrap(); + assert_eq!(page.slug, "file-with-space"); + assert_eq!(page.permalink, config.make_permalink(&page.slug)); + } + + #[test] + fn can_specify_summary() { + let config = Config::default(); + let content = r#" ++++ ++++ +Hello world +"#.to_string(); + let res = Page::parse(Path::new("hello.md"), &content, &config); + assert!(res.is_ok()); + let mut page = res.unwrap(); + page.render_markdown(&HashMap::default(), &Tera::default(), &config).unwrap(); + assert_eq!(page.summary, Some("Hello world
\n".to_string())); + } + + #[test] + fn page_with_assets_gets_right_parent_path() { + let tmp_dir = TempDir::new("example").expect("create temp dir"); + let path = tmp_dir.path(); + create_dir(&path.join("content")).expect("create content temp dir"); + create_dir(&path.join("content").join("posts")).expect("create posts temp dir"); + let nested_path = path.join("content").join("posts").join("assets"); + create_dir(&nested_path).expect("create nested temp dir"); + File::create(nested_path.join("index.md")).unwrap(); + File::create(nested_path.join("example.js")).unwrap(); + File::create(nested_path.join("graph.jpg")).unwrap(); + File::create(nested_path.join("fail.png")).unwrap(); + + let res = Page::parse( + nested_path.join("index.md").as_path(), + "+++\nurl=\"hey\"+++\n", + &Config::default() + ); + assert!(res.is_ok()); + let page = res.unwrap(); + assert_eq!(page.parent_path, path.join("content").join("posts")); + } + + #[test] + fn errors_file_not_named_index_with_assets() { + let tmp_dir = TempDir::new("example").expect("create temp dir"); + File::create(tmp_dir.path().join("something.md")).unwrap(); + File::create(tmp_dir.path().join("example.js")).unwrap(); + File::create(tmp_dir.path().join("graph.jpg")).unwrap(); + File::create(tmp_dir.path().join("fail.png")).unwrap(); + + let page = Page::from_file(tmp_dir.path().join("something.md"), &Config::default()); + assert!(page.is_err()); + } +} diff --git a/src/markdown.rs b/src/markdown.rs index f32cf89..f4a236b 100644 --- a/src/markdown.rs +++ b/src/markdown.rs @@ -347,14 +347,14 @@ mod tests { use super::{markdown_to_html, parse_shortcode}; #[test] - fn test_parse_simple_shortcode_one_arg() { + fn can_parse_simple_shortcode_one_arg() { let (name, args) = parse_shortcode(r#"{{ youtube(id="w7Ft2ymGmfc") }}"#); assert_eq!(name, "youtube"); assert_eq!(args["id"], "w7Ft2ymGmfc"); } #[test] - fn test_parse_simple_shortcode_several_arg() { + fn can_parse_simple_shortcode_several_arg() { let (name, args) = parse_shortcode(r#"{{ youtube(id="w7Ft2ymGmfc", autoplay=true) }}"#); assert_eq!(name, "youtube"); assert_eq!(args["id"], "w7Ft2ymGmfc"); @@ -362,7 +362,7 @@ mod tests { } #[test] - fn test_parse_block_shortcode_several_arg() { + fn can_parse_block_shortcode_several_arg() { let (name, args) = parse_shortcode(r#"{% youtube(id="w7Ft2ymGmfc", autoplay=true) %}"#); assert_eq!(name, "youtube"); assert_eq!(args["id"], "w7Ft2ymGmfc"); @@ -370,13 +370,13 @@ mod tests { } #[test] - fn test_markdown_to_html_simple() { + fn can_do_markdown_to_html_simple() { let res = markdown_to_html("hello", &HashMap::new(), &Tera::default(), &Config::default()).unwrap(); assert_eq!(res, "hello
\n"); } #[test] - fn test_markdown_to_html_code_block_highlighting_off() { + fn doesnt_highlight_code_block_with_highlighting_off() { let mut config = Config::default(); config.highlight_code = Some(false); let res = markdown_to_html("```\n$ gutenberg server\n```", &HashMap::new(), &Tera::default(), &config).unwrap(); @@ -387,7 +387,7 @@ mod tests { } #[test] - fn test_markdown_to_html_code_block_no_lang() { + fn can_highlight_code_block_no_lang() { let res = markdown_to_html("```\n$ gutenberg server\n$ ping\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap(); assert_eq!( res, @@ -396,7 +396,7 @@ mod tests { } #[test] - fn test_markdown_to_html_code_block_with_lang() { + fn can_highlight_code_block_with_lang() { let res = markdown_to_html("```python\nlist.append(1)\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap(); assert_eq!( res, @@ -405,7 +405,7 @@ mod tests { } #[test] - fn test_markdown_to_html_code_block_with_unknown_lang() { + fn can_higlight_code_block_with_unknown_lang() { let res = markdown_to_html("```yolo\nlist.append(1)\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap(); // defaults to plain text assert_eq!( @@ -415,7 +415,7 @@ mod tests { } #[test] - fn test_markdown_to_html_with_shortcode() { + fn can_render_shortcode() { let res = markdown_to_html(r#" Hello @@ -426,7 +426,7 @@ Hello } #[test] - fn test_markdown_to_html_with_several_shortcode_in_row() { + fn can_render_several_shortcode_in_row() { let res = markdown_to_html(r#" Hello @@ -446,13 +446,13 @@ Hello } #[test] - fn test_markdown_to_html_shortcode_in_code_block() { + fn doesnt_render_shortcode_in_code_block() { let res = markdown_to_html(r#"```{{ youtube(id="w7Ft2ymGmfc") }}```"#, &HashMap::new(), &GUTENBERG_TERA, &Config::default()).unwrap(); assert_eq!(res, "{{ youtube(id="w7Ft2ymGmfc") }}
{{ body }} - {{ author}}").unwrap(); @@ -466,13 +466,13 @@ A quote } #[test] - fn test_markdown_to_html_unknown_shortcode() { + fn errors_rendering_unknown_shortcode() { let res = markdown_to_html("{{ hello(flash=true) }}", &HashMap::new(), &Tera::default(), &Config::default()); assert!(res.is_err()); } #[test] - fn test_markdown_to_html_relative_link_exists() { + fn can_make_valid_relative_link() { let mut permalinks = HashMap::new(); permalinks.insert("pages/about.md".to_string(), "https://vincent.is/about".to_string()); let res = markdown_to_html( @@ -488,7 +488,7 @@ A quote } #[test] - fn test_markdown_to_html_relative_links_with_anchors() { + fn can_make_relative_links_with_anchors() { let mut permalinks = HashMap::new(); permalinks.insert("pages/about.md".to_string(), "https://vincent.is/about".to_string()); let res = markdown_to_html( @@ -504,25 +504,25 @@ A quote } #[test] - fn test_markdown_to_html_relative_link_inexistant() { + fn errors_relative_link_inexistant() { let res = markdown_to_html("[rel link](./pages/about.md)", &HashMap::new(), &Tera::default(), &Config::default()); assert!(res.is_err()); } #[test] - fn test_markdown_to_html_add_id_to_headers() { + fn can_add_id_to_headers() { let res = markdown_to_html(r#"# Hello"#, &HashMap::new(), &GUTENBERG_TERA, &Config::default()).unwrap(); assert_eq!(res, "
Hello world
\n".to_string()); -} - -#[test] -fn test_can_find_one_parent_directory() { - let content = r#" -+++ -title = "Hello" -description = "hey there" -slug = "hello-world" -+++ -Hello world"#; - let res = Page::parse(Path::new("content/posts/intro.md"), content, &Config::default()); - assert!(res.is_ok()); - let mut page = res.unwrap(); - page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); - assert_eq!(page.components, vec!["posts".to_string()]); -} - -#[test] -fn test_can_find_multiple_parent_directories() { - let content = r#" -+++ -title = "Hello" -description = "hey there" -slug = "hello-world" -+++ -Hello world"#; - let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &Config::default()); - assert!(res.is_ok()); - let mut page = res.unwrap(); - page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); - assert_eq!(page.components, vec!["posts".to_string(), "intro".to_string()]); -} - -#[test] -fn test_can_make_url_from_sections_and_slug() { - let content = r#" -+++ -title = "Hello" -description = "hey there" -slug = "hello-world" -+++ -Hello world"#; - let mut conf = Config::default(); - conf.base_url = "http://hello.com/".to_string(); - let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &conf); - assert!(res.is_ok()); - let mut page = res.unwrap(); - page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); - assert_eq!(page.path, "posts/intro/hello-world"); - assert_eq!(page.permalink, "http://hello.com/posts/intro/hello-world"); -} - -#[test] -fn test_can_make_permalink_with_non_trailing_slash_base_url() { - let content = r#" -+++ -title = "Hello" -description = "hey there" -slug = "hello-world" -+++ -Hello world"#; - let mut conf = Config::default(); - conf.base_url = "http://hello.com".to_string(); - let res = Page::parse(Path::new("content/posts/intro/hello-world.md"), content, &conf); - assert!(res.is_ok()); - let mut page = res.unwrap(); - page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); - assert_eq!(page.path, "posts/intro/hello-world"); - assert_eq!(page.permalink, format!("{}{}", conf.base_url, "/posts/intro/hello-world")); -} - -#[test] -fn test_can_make_url_from_slug_only() { - let content = r#" -+++ -title = "Hello" -description = "hey there" -slug = "hello-world" -+++ -Hello world"#; - let res = Page::parse(Path::new("start.md"), content, &Config::default()); - assert!(res.is_ok()); - let mut page = res.unwrap(); - page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); - assert_eq!(page.path, "hello-world"); - assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "hello-world")); -} - -#[test] -fn test_errors_on_invalid_front_matter_format() { - let content = r#" -title = "Hello" -description = "hey there" -slug = "hello-world" -+++ -Hello world"#; - let res = Page::parse(Path::new("start.md"), content, &Config::default()); - assert!(res.is_err()); -} - -#[test] -fn test_can_make_slug_from_non_slug_filename() { - let content = r#" -+++ -title = "Hello" -description = "hey there" -+++ -Hello world"#; - let res = Page::parse(Path::new("file with space.md"), content, &Config::default()); - assert!(res.is_ok()); - let mut page = res.unwrap(); - page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); - assert_eq!(page.slug, "file-with-space"); - assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "file-with-space")); -} - -#[test] -fn test_trim_slug_if_needed() { - let content = r#" -+++ -title = "Hello" -description = "hey there" -+++ -Hello world"#; - let res = Page::parse(Path::new(" file with space.md"), content, &Config::default()); - assert!(res.is_ok()); - let mut page = res.unwrap(); - page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); - assert_eq!(page.slug, "file-with-space"); - assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "file-with-space")); -} - -#[test] -fn test_automatic_summary_is_empty_string() { - let content = r#" -+++ -title = "Hello" -description = "hey there" -+++ -Hello world"#.to_string(); - let res = Page::parse(Path::new("hello.md"), &content, &Config::default()); - assert!(res.is_ok()); - let mut page = res.unwrap(); - page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); - assert_eq!(page.summary, None); -} - -#[test] -fn test_can_specify_summary() { - let content = r#" -+++ -title = "Hello" -description = "hey there" -+++ -Hello world - -"#.to_string(); - let res = Page::parse(Path::new("hello.md"), &content, &Config::default()); - assert!(res.is_ok()); - let mut page = res.unwrap(); - page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); - assert_eq!(page.summary, Some("Hello world
\n".to_string())); -} - -#[test] -fn test_can_auto_detect_when_highlighting_needed() { - let content = r#" -+++ -title = "Hello" -description = "hey there" -+++ -``` -Hey there -``` -"#.to_string(); - let mut config = Config::default(); - config.highlight_code = Some(true); - let res = Page::parse(Path::new("hello.md"), &content, &config); - assert!(res.is_ok()); - let mut page = res.unwrap(); - page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap(); - assert!(page.content.starts_with("