// Copyright © 2024 Shokunin Static Site Generator. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT
#[cfg(test)]
mod tests {
use comrak::ComrakOptions;
use quick_xml::Writer;
use regex::Regex;
use ssg::utilities::{
backup::backup_file,
directory::{
cleanup_directory, create_comrak_options, create_directory,
directory, extract_front_matter, find_html_files,
format_header_with_id_class, move_output_directory,
to_title_case, update_class_attributes,
},
element::write_element,
minification::{
minify_html, minify_html_files, write_minified_html,
},
};
use std::{
error::Error,
fs::{self, File},
io::{self, Cursor, Read, Write},
path::{Path, PathBuf},
};
use tempfile::tempdir;
#[test]
fn test_directory_existing_directory() {
let temp = tempdir().unwrap();
let temp_path = temp.path().to_owned();
let name = "existing_directory";
let result = directory(&temp_path, name);
assert!(
result.is_ok(),
"Should not return an error for an existing directory"
);
}
#[test]
fn test_directory_existing_file() {
let temp = tempdir().unwrap();
let file_path = temp.path().join("file.txt");
fs::write(&file_path, "Some content").unwrap();
let name = "existing_file";
let result = directory(&file_path, name);
assert!(
result.is_err(),
"Should return an error for an existing file"
);
}
#[test]
fn test_directory_create_directory() {
let temp = tempdir().unwrap();
let new_dir = temp.path().join("new_directory");
let name = "new_directory";
let result = directory(&new_dir, name);
assert!(
result.is_ok(),
"Should not return an error for a newly created directory"
);
assert!(
new_dir.exists() && new_dir.is_dir(),
"New directory should be created"
);
}
#[test]
fn test_directory_create_nested_directory() {
let temp = tempdir().unwrap();
let new_nested_dir =
temp.path().join("nested").join("directory");
let name = "nested_directory";
let result = directory(&new_nested_dir, name);
assert!(
result.is_ok(),
"Should not return an error for a newly created nested directory"
);
assert!(
new_nested_dir.exists() && new_nested_dir.is_dir(),
"New nested directory should be created"
);
}
#[test]
fn test_move_output_directory() -> io::Result<()> {
// Setup: Create a dummy output directory and a dummy file inside it.
let out_dir = Path::new("temp_out_dir");
let dummy_file = out_dir.join("dummy_file.txt");
fs::create_dir_all(out_dir)?;
File::create(dummy_file)?;
// Call the function to test.
let site_name = "My Test Site";
move_output_directory(site_name, out_dir)?;
// Check that the output directory has been moved to the expected location.
let public_dir = Path::new("public");
let expected_new_dir =
public_dir.join(site_name.replace(' ', "_"));
assert!(
expected_new_dir.exists(),
"The directory was not moved to the expected location."
);
// Check that the dummy file still exists at the new location.
let expected_new_file = expected_new_dir.join("dummy_file.txt");
assert!(expected_new_file.exists(), "The contents of the directory were not preserved during the move.");
// Cleanup: Remove the public directory that was created during the test.
fs::remove_dir_all(public_dir)?;
Ok(())
}
#[test]
fn test_minify_html_files() -> io::Result<()> {
// Setup: Create a dummy output directory and a dummy HTML file inside it.
let out_dir = Path::new("tests/test_output");
let dummy_file = out_dir.join("dummy_file.html");
fs::create_dir_all(out_dir)?;
let mut file = File::create(&dummy_file)?;
file.write_all(
b"\n
\nHello, World!\n\n",
)?;
// Call the function to test.
minify_html_files(out_dir)?;
// Check that the HTML file still exists at the original location.
assert!(
dummy_file.exists(),
"The original HTML file does not exist."
);
// Check that the HTML file has been minified.
// This assumes that your minify_html function simply removes newline characters.
// Update this to match the actual behaviour of your minify_html function.
let minified_contents = fs::read_to_string(&dummy_file)?;
assert_eq!(
minified_contents,
"Hello, World!"
);
// Cleanup: Remove the temp output directory that was created during the test.
fs::remove_dir_all(out_dir)?;
Ok(())
}
#[test]
fn test_find_html_files() -> io::Result<()> {
// Setup: Create a directory with a few .html files and a subdirectory
// with a few more .html files.
let dir = Path::new("temp_dir");
let sub_dir = dir.join("sub_dir");
fs::create_dir_all(&sub_dir)?;
let file_paths = vec![
dir.join("file1.html"),
dir.join("file2.html"),
sub_dir.join("file3.html"),
sub_dir.join("file4.html"),
];
for file_path in &file_paths {
let mut file = File::create(file_path)?;
file.write_all(b"")?;
}
// Call the function to test.
let found_html_files = find_html_files(dir)?;
// Check that the function found all of the .html files.
let mut expected_html_files: Vec = file_paths;
expected_html_files.sort_unstable();
let mut found_html_files_sorted = found_html_files;
found_html_files_sorted.sort_unstable();
assert_eq!(
found_html_files_sorted,
expected_html_files,
"The function did not find all of the expected .html files."
);
// Cleanup: Remove the directory that was created during the test.
fs::remove_dir_all(dir)?;
Ok(())
}
#[test]
fn test_minify_html() -> io::Result<()> {
// Setup: Create a dummy HTML file.
let file_path = Path::new("temp_html_file.html");
let mut file = File::create(file_path)?;
write!(file, "\n\n\n Test Page\n\n\n Hello, world!
\n\n")?;
// Call the function to test.
let minified_content = minify_html(file_path)?;
// Check that the HTML has been minified.
// This check will depend on the exact settings you're using in your minify function.
// Here, we're just checking that the minified content is shorter than the original content.
let original_content = fs::read_to_string(file_path)?;
assert!(
minified_content.len() < original_content.len(),
"The HTML was not minified."
);
// Cleanup: Remove the HTML file that was created during the test.
fs::remove_file(file_path)?;
Ok(())
}
#[test]
fn test_backup_file() -> io::Result<()> {
// Setup: Create a dummy file.
let file_path = Path::new("temp_file.txt");
let mut file = File::create(file_path)?;
file.write_all(b"Some text for the dummy file")?;
// Call the function to test.
let backup_path = backup_file(file_path)?;
// Check that the backup file was created and that it has the expected content.
assert!(backup_path.exists(), "Backup file was not created.");
let backup_content = fs::read_to_string(&backup_path)?;
assert_eq!(
backup_content, "Some text for the dummy file",
"Backup file content does not match original file content."
);
// Cleanup: Remove the original and backup file.
fs::remove_file(file_path)?;
fs::remove_file(backup_path)?;
Ok(())
}
#[test]
fn test_write_minified_html() -> io::Result<()> {
// Setup: Define a dummy file path and minified HTML.
let file_path = Path::new("temp_minified_html_file.html");
let minified_html = "TestHello, World!
";
// Call the function to test.
write_minified_html(file_path, minified_html)?;
// Check that the file now exists.
assert!(file_path.exists(), "The file was not created.");
// Check that the file contains the expected minified HTML.
let mut file = File::open(file_path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(
contents, minified_html,
"The file does not contain the expected minified HTML."
);
// Cleanup: Remove the file that was created during the test.
fs::remove_file(file_path)?;
Ok(())
}
#[test]
fn test_cleanup_directory_success() -> Result<(), Box> {
// Setup: Create some dummy directories with dummy files in them.
let dir1 = Path::new("temp_dir1");
let dummy_file1 = dir1.join("dummy_file1.txt");
fs::create_dir_all(dir1)?;
File::create(dummy_file1)?;
let dir2 = Path::new("temp_dir2");
let dummy_file2 = dir2.join("dummy_file2.txt");
fs::create_dir_all(dir2)?;
File::create(dummy_file2)?;
let directories: Vec<&Path> = vec![dir1, dir2];
// Call the function to test.
cleanup_directory(&directories)?;
// Check that the directories no longer exist.
assert!(!dir1.exists(), "The directory1 was not cleaned up.");
assert!(!dir2.exists(), "The directory2 was not cleaned up.");
Ok(())
}
#[test]
fn test_cleanup_directory_ignore_nonexistent(
) -> Result<(), Box> {
// Setup: Use a path that doesn't exist.
let nonexistent_dir = Path::new("nonexistent_dir");
let directories: Vec<&Path> = vec![nonexistent_dir];
// Call the function to test.
cleanup_directory(&directories)?;
// Check that we didn't get an error even though the directory doesn't exist.
assert!(
!nonexistent_dir.exists(),
"The nonexistent directory was somehow created."
);
Ok(())
}
#[test]
fn test_create_directory() -> Result<(), Box> {
// Setup: Define some directories to test with.
let dir1 = Path::new("tests/temp_dir1");
let dir2 = Path::new("tests/temp_dir2");
let dir3 = Path::new("tests/temp_dir3");
// Create dir1 and dir2, but leave dir3 non-existent for now.
fs::create_dir(dir1)?;
fs::create_dir(dir2)?;
let dirs = vec![dir1, dir2, dir3];
// Call the function to test.
create_directory(&dirs)?;
// Check that all directories exist.
assert!(dir1.exists(), "dir1 should still exist.");
assert!(dir2.exists(), "dir2 should still exist.");
assert!(dir3.exists(), "dir3 should have been created.");
// Cleanup: Remove the directories that were created during the test.
fs::remove_dir(dir1)?;
fs::remove_dir(dir2)?;
fs::remove_dir(dir3)?;
Ok(())
}
#[test]
fn test_write_element() -> Result<(), Box> {
let mut writer = Writer::new(Cursor::new(Vec::new()));
let name = "element";
let value = "value";
write_element(&mut writer, name, value)?;
let result =
String::from_utf8(writer.into_inner().into_inner())?;
assert_eq!(result, "value");
Ok(())
}
#[test]
fn test_write_element_empty_value() -> Result<(), Box> {
let mut writer = Writer::new(Cursor::new(Vec::new()));
let name = "element";
let value = "";
write_element(&mut writer, name, value)?;
let result =
String::from_utf8(writer.into_inner().into_inner())?;
assert_eq!(result, "");
Ok(())
}
#[test]
fn test_write_element_special_chars() -> Result<(), Box>
{
let mut writer = Writer::new(Cursor::new(Vec::new()));
let name = "element";
let value = "<>&\"'";
write_element(&mut writer, name, value)?;
let result =
String::from_utf8(writer.into_inner().into_inner())?;
assert_eq!(result, "<>&\"'");
Ok(())
}
#[test]
fn test_to_title_case_single_word() {
assert_eq!(to_title_case("hello"), "Hello");
assert_eq!(to_title_case("WORLD"), "WORLD");
assert_eq!(to_title_case("rust"), "Rust");
}
#[test]
fn test_to_title_case_multiple_words() {
assert_eq!(to_title_case("hello world"), "Hello World");
assert_eq!(to_title_case("HELLO WORLD"), "HELLO WORLD");
assert_eq!(to_title_case("hello WORLD"), "Hello WORLD");
assert_eq!(
to_title_case("Rust programming language"),
"Rust Programming Language"
);
}
#[test]
fn test_to_title_case_empty_string() {
assert_eq!(to_title_case(""), "");
}
#[test]
fn test_to_title_case_only_spaces() {
assert_eq!(to_title_case(""), "");
}
#[test]
fn test_to_title_case_leading_trailing_spaces() {
assert_eq!(to_title_case(" hello "), " Hello ");
assert_eq!(to_title_case(" hello world "), " Hello World ");
}
#[test]
fn test_test_format_header_with_id_class() {
let id_regex = Regex::new(r"[^a-z0-9]+").unwrap();
let test_cases = vec![
(
"This is a test
",
"This is a test
"
),
(
"Another Test
",
"Another Test
"
),
(
"Test with special characters!@#$%^&*
",
"Test with special characters!@#$%^&*
"
),
(
"Test with multiple spaces
",
"Test with multiple spaces
"
),
(
"Test_with_underscores
",
"Test_with_underscores
"
),
(
"Test-with-dashes
",
"Test-with-dashes
"
),
];
for (input, expected_output) in test_cases {
assert_eq!(
format_header_with_id_class(input, &id_regex),
expected_output,
);
}
}
#[test]
fn test_extract_front_matter_yaml() {
let content = "---\ntitle: Hello\n---\nThis is the content.";
assert_eq!(
extract_front_matter(content),
"This is the content."
);
}
#[test]
fn test_extract_front_matter_toml() {
let content = "+++\ntitle = 'Hello'\n+++\nThis is the content.";
assert_eq!(
extract_front_matter(content),
"This is the content."
);
}
#[test]
fn test_extract_front_matter_json() {
let content =
"{\n\"title\": \"Hello\"\n}\nThis is the content.";
assert_eq!(
extract_front_matter(content),
"\nThis is the content."
);
}
#[test]
fn test_extract_front_matter_none() {
let content = "This is the content.";
assert_eq!(
extract_front_matter(content),
"This is the content."
);
}
#[test]
fn test_extract_front_matter_incomplete_yaml() {
let content = "---\ntitle: Hello\nThis is the content.";
assert_eq!(extract_front_matter(content), "");
}
#[test]
fn test_extract_front_matter_incomplete_toml() {
let content = "+++\ntitle = 'Hello'\nThis is the content.";
assert_eq!(extract_front_matter(content), "");
}
#[test]
fn test_extract_front_matter_incomplete_json() {
let content = "{\n\"title\": \"Hello\"\nThis is the content.";
assert_eq!(extract_front_matter(content), "");
}
#[test]
fn test_create_comrak_options() {
let options = create_comrak_options();
assert!(options.extension.autolink);
assert!(options.extension.description_lists);
assert!(options.extension.footnotes);
assert_eq!(
options.extension.front_matter_delimiter,
Some("---".to_owned())
);
// assert_eq!(options.extension.header_ids, Some("".to_string()));
assert!(options.extension.strikethrough);
assert!(options.extension.superscript);
assert!(options.extension.table);
assert!(options.extension.tagfilter);
assert!(options.extension.tasklist);
assert!(options.parse.smart);
assert!(options.render.github_pre_lang);
assert!(!options.render.hardbreaks);
assert!(options.render.unsafe_);
}
#[test]
fn test_default_comrak_options() {
let mut options = ComrakOptions::default();
options.extension.autolink = true;
assert!(options.extension.autolink);
assert!(!options.extension.description_lists);
assert!(!options.extension.footnotes);
assert_eq!(options.extension.front_matter_delimiter, None);
assert_eq!(options.extension.header_ids, None);
assert!(!options.extension.strikethrough);
assert!(!options.extension.superscript);
assert!(!options.extension.table);
assert!(!options.extension.tagfilter);
assert!(!options.extension.tasklist);
assert!(!options.parse.smart);
assert!(!options.render.github_pre_lang);
assert!(!options.render.hardbreaks);
assert!(!options.render.unsafe_);
}
#[test]
fn test_update_class_attributes_no_class() {
let line = "";
let class_regex =
Regex::new(r"\.class="([^&]*)"").unwrap();
let img_regex = Regex::new(r"(img[^>]*)\s?/>").unwrap();
assert_eq!(
update_class_attributes(line, &class_regex, &img_regex),
String::from(line)
);
}
#[test]
fn test_update_class_attributes_with_class() {
let line = "";
let class_regex =
Regex::new(r"\.class="([^&]*)"").unwrap();
let img_regex = Regex::new(r"(img[^>]*)\s?/>").unwrap();
assert_eq!(
update_class_attributes(line, &class_regex, &img_regex),
String::from(line)
);
}
#[test]
fn test_update_class_attributes_error_handling() {
let line = "";
let class_regex =
Regex::new(r"\.class="([^&]*)"").unwrap();
let img_regex = Regex::new(r"(img[^>]*)\s?/>").unwrap();
assert_eq!(
update_class_attributes(
".class="non_matching_class"",
&class_regex,
&img_regex
),
String::from(".class="non_matching_class"")
);
assert_eq!(
update_class_attributes(line, &class_regex, &img_regex),
String::from(line)
);
}
}