diff --git a/crates/zx_document_core/src/epub.rs b/crates/zx_document_core/src/epub.rs index 11bca9a..ec01ffb 100644 --- a/crates/zx_document_core/src/epub.rs +++ b/crates/zx_document_core/src/epub.rs @@ -655,4 +655,141 @@ mod tests { assert_eq!(chapters[1].title, "Chapter 2"); assert_eq!(chapters[2].title, "Chapter 3"); } + + // ── strip_html tests ── + + #[test] + fn test_strip_html_removes_tags() { + assert_eq!(strip_html("

Hello World

"), "Hello World"); + } + + #[test] + fn test_strip_html_collapses_whitespace() { + assert_eq!(strip_html("

Hello World

"), "Hello World"); + assert_eq!(strip_html("
\n Line 1\n Line 2\n
"), "Line 1 Line 2"); + } + + #[test] + fn test_strip_html_handles_nested_tags() { + assert_eq!(strip_html("

Bold text

"), "Bold text"); + } + + #[test] + fn test_strip_html_handles_self_closing_tags() { + assert_eq!(strip_html("Text
more
text"), "Textmoretext"); + } + + #[test] + fn test_strip_html_handles_attributes() { + assert_eq!(strip_html("

Hello

"), "Hello"); + } + + #[test] + fn test_strip_html_empty_input() { + assert_eq!(strip_html(""), ""); + } + + #[test] + fn test_strip_html_plain_text_unchanged() { + assert_eq!(strip_html("Plain text, no HTML tags."), "Plain text, no HTML tags."); + } + + // ── extract_epub_chapter_texts tests ── + + fn epub_with_chapter_text(title: &str, chapters: &[(&str, &str)]) -> Vec { + let mut buf = std::io::Cursor::new(Vec::new()); + { + let mut zip = zip::ZipWriter::new(&mut buf); + let opts = zip::write::SimpleFileOptions::default() + .compression_method(zip::CompressionMethod::Stored); + + zip.start_file("mimetype", opts).unwrap(); + zip.write_all(b"application/epub+zip").unwrap(); + + zip.start_file("META-INF/container.xml", opts).unwrap(); + zip.write_all( + b"\n\n \n \n \n" + ).unwrap(); + + let mut opf = format!( + "\n\n\n {}\n\n\n \n", + title + ); + for (i, (href, _)) in chapters.iter().enumerate() { + opf.push_str(&format!(" \n", i, href)); + } + opf.push_str("\n\n"); + for i in 0..chapters.len() { + opf.push_str(&format!(" \n", i)); + } + opf.push_str("\n"); + zip.start_file("OEBPS/content.opf", opts).unwrap(); + zip.write_all(opf.as_bytes()).unwrap(); + + let mut ncx = String::from("\n\n\n"); + for (i, (href, _)) in chapters.iter().enumerate() { + ncx.push_str(&format!( + " \n Ch {}\n \n \n", + i, i + 1, i + 1, href + )); + } + ncx.push_str("\n"); + zip.start_file("toc.ncx", opts).unwrap(); + zip.write_all(ncx.as_bytes()).unwrap(); + + for (href, content) in chapters { + zip.start_file(*href, opts).unwrap(); + zip.write_all(content.as_bytes()).unwrap(); + } + + zip.finish().unwrap(); + } + buf.into_inner() + } + + #[test] + fn test_extract_epub_chapter_texts_with_html() { + let data = epub_with_chapter_text("Test", &[ + ("OEBPS/ch1.xhtml", "

Chapter one content.

"), + ("OEBPS/ch2.xhtml", "

Title

Chapter two.

"), + ]); + let path = write_temp(&data, "chapter_text.epub"); + let texts = extract_epub_chapter_texts(&path).unwrap(); + assert_eq!(texts.len(), 2); + assert_eq!(texts[0].text, "Chapter one content."); + assert_eq!(texts[1].text, "TitleChapter two."); + } + + #[test] + fn test_extract_epub_chapter_texts_empty_chapter_filtered() { + let data = epub_with_chapter_text("Empty", &[ + ("OEBPS/ch1.xhtml", ""), + ("OEBPS/ch2.xhtml", "

Not empty

"), + ]); + let path = write_temp(&data, "empty_ch.epub"); + let texts = extract_epub_chapter_texts(&path).unwrap(); + assert_eq!(texts.len(), 1); + assert_eq!(texts[0].text, "Not empty"); + } + + #[test] + fn test_extract_epub_chapter_texts_all_empty() { + let data = epub_with_chapter_text("AllEmpty", &[ + ("OEBPS/ch1.xhtml", ""), + ]); + let path = write_temp(&data, "all_empty.epub"); + let texts = extract_epub_chapter_texts(&path).unwrap(); + assert!(texts.is_empty()); + } + + #[test] + fn test_extract_epub_chapter_texts_plain_text_preserved() { + let data = epub_with_chapter_text("Plain", &[ + ("OEBPS/ch1.xhtml", "No HTML tags, just plain text."), + ]); + let path = write_temp(&data, "plain.epub"); + let texts = extract_epub_chapter_texts(&path).unwrap(); + assert_eq!(texts.len(), 1); + assert_eq!(texts[0].text, "No HTML tags, just plain text."); + } }