use text_document::{
BlockFormat, MarkdownExportOptions, MoveMode, PlainTextExportOptions, TextDocument,
};
const TWO_CHAPTERS: &str = "\
# Chapter One
The first chapter.
{page_break_before=true}
# Chapter Two
The second chapter.";
fn doc() -> TextDocument {
let d = TextDocument::new();
d.set_djot_sync(TWO_CHAPTERS).expect("set_djot");
d
}
#[test]
fn html_emits_both_css_spellings() {
let html = doc().to_html().expect("to_html");
assert!(html.contains("break-before: page"), "{html}");
assert!(html.contains("page-break-before: always"), "{html}");
}
#[test]
fn the_break_lands_on_the_second_chapter_and_not_the_first() {
let html = doc().to_html().expect("to_html");
let one = html.find("Chapter One").expect("chapter one");
let two = html.find("Chapter Two").expect("chapter two");
let brk = html.find("break-before: page").expect("a break");
assert!(
brk > one && brk < two,
"the break must sit between the two chapters, not before both:\n{html}"
);
}
#[test]
fn latex_emits_a_newpage() {
let tex = doc().to_latex("article", true).expect("to_latex");
assert!(tex.contains("\\newpage"), "{tex}");
let brk = tex.find("\\newpage").expect("a break");
let two = tex.find("Chapter Two").expect("chapter two");
assert!(brk < two, "the break must precede its chapter:\n{tex}");
}
#[test]
fn djot_round_trips_the_flag() {
let dj = doc().to_djot().expect("to_djot");
assert!(dj.contains("page_break_before=true"), "{dj}");
}
#[test]
fn plain_text_is_unchanged_by_default() {
let txt = doc().to_plain_text().expect("to_plain_text");
assert!(!txt.contains('\u{000C}'), "{txt:?}");
}
#[test]
fn plain_text_emits_a_form_feed_when_asked() {
let txt = doc()
.to_plain_text_with(PlainTextExportOptions::presentation())
.expect("to_plain_text_with");
let brk = txt.find('\u{000C}').expect("a form feed");
let two = txt.find("Chapter Two").expect("chapter two");
assert!(brk < two, "the form feed must lead its block:\n{txt:?}");
}
#[test]
fn the_form_feed_is_the_only_difference() {
let d = doc();
let plain = d.to_plain_text().expect("plain");
let paged = d
.to_plain_text_with(PlainTextExportOptions {
quote_indent: false,
page_breaks: true,
strip_images: true,
endnote_footnotes: true,
})
.expect("paged");
assert_eq!(paged.replace('\u{000C}', ""), plain);
}
#[test]
fn markdown_is_clean_by_default() {
let md = doc().to_markdown().expect("to_markdown");
assert!(
!md.contains("<div"),
"raw HTML has no business in a Markdown export nobody asked to paginate:\n{md}"
);
}
#[test]
fn markdown_emits_a_raw_html_break_when_asked() {
let md = doc()
.to_markdown_with(MarkdownExportOptions {
page_breaks: true,
..Default::default()
})
.expect("to_markdown_with");
assert!(md.contains("break-before: page"), "{md}");
let brk = md.find("<div").expect("a break");
let two = md.find("Chapter Two").expect("chapter two");
assert!(brk < two, "the break must precede its chapter:\n{md}");
}
#[test]
fn a_list_item_normalises_the_attribute_away_like_every_other() {
let d = TextDocument::new();
d.set_djot_sync("- one\n- two\n\n{page_break_before=true}\n- three")
.expect("set_djot");
assert!(!d.to_djot().expect("to_djot").contains("page_break_before"));
}
#[test]
fn a_break_inside_a_list_ends_the_run_rather_than_vanishing() {
let d = TextDocument::new();
d.set_djot_sync("- one\n- two\n- three").expect("set_djot");
let c = d.cursor();
c.set_position(9, MoveMode::MoveAnchor);
c.set_block_format(&BlockFormat {
page_break_before: Some(true),
..Default::default()
})
.expect("set_block_format");
let tex = d.to_latex("article", true).expect("to_latex");
let brk = tex
.find("\\newpage")
.unwrap_or_else(|| panic!("no break in:\n{tex}"));
let three = tex.find("three").expect("third item");
assert!(brk < three, "{tex}");
assert_eq!(
tex.matches("\\begin{itemize}").count(),
2,
"the run must be split in two:\n{tex}"
);
}
#[test]
fn latex_centres_a_centred_block() {
let d = TextDocument::new();
d.set_djot_sync("{alignment=center}\n#").expect("set_djot");
let tex = d.to_latex("article", true).expect("to_latex");
assert!(tex.contains("\\begin{center}"), "{tex}");
assert!(tex.contains("\\end{center}"), "{tex}");
}
#[test]
fn latex_right_aligns_a_right_aligned_block() {
let d = TextDocument::new();
d.set_djot_sync("{alignment=right}\nTo the right.")
.expect("set_djot");
let tex = d.to_latex("article", true).expect("to_latex");
assert!(tex.contains("\\begin{flushright}"), "{tex}");
}
#[test]
fn latex_leaves_the_default_alignments_unwrapped() {
let d = TextDocument::new();
d.set_djot_sync("{alignment=justify}\nOrdinary prose.")
.expect("set_djot");
let tex = d.to_latex("article", true).expect("to_latex");
assert!(!tex.contains("\\begin{center}"), "{tex}");
assert!(!tex.contains("\\begin{flushright}"), "{tex}");
}
#[test]
fn latex_emits_a_blocks_own_space_above() {
let d = TextDocument::new();
d.set_djot_sync("{top_margin=96}\nAfter a gap.")
.expect("set_djot");
let tex = d.to_latex("article", true).expect("to_latex");
assert!(tex.contains("\\vspace*{72.0pt}"), "{tex}");
}
#[test]
fn latex_sets_an_epigraph_apart_from_a_plain_quotation() {
let d = TextDocument::new();
d.set_djot_sync(
"> {semantic_role=epigraph}\n> All happy families.\n>\n> {alignment=right}\n> Tolstoy",
)
.expect("set_djot");
let tex = d.to_latex("article", true).expect("to_latex");
assert!(tex.contains("\\begin{quote}"), "{tex}");
assert!(
tex.contains("\\itshape"),
"the quotation itself must be italic:\n{tex}"
);
let italic = tex.find("\\itshape").expect("italics");
let attribution = tex.find("Tolstoy").expect("attribution");
let flush = tex
.find("\\begin{flushright}")
.expect("right-aligned source");
assert!(
italic < flush && flush < attribution,
"the attribution is right-aligned and outside the italics:\n{tex}"
);
}
#[test]
fn a_plain_quotation_is_not_italicised() {
let d = TextDocument::new();
d.set_djot_sync("> Just a quotation.").expect("set_djot");
let tex = d.to_latex("article", true).expect("to_latex");
assert!(tex.contains("\\begin{quote}"), "{tex}");
assert!(!tex.contains("\\itshape"), "{tex}");
}