use lex_core::lex::parsing::{parse_document, ContentItem};
use lex_core::lex::testing::lexplore::Lexplore;
use lex_core::lex::testing::workspace_path;
use lex_core::lex::testing::{
assert_ast, InlineAssertion, InlineExpectation, ReferenceExpectation, TextMatch,
};
#[test]
fn test_real_content_extraction() {
let input = "First paragraph with numbers 123 and symbols (like this).\n\nSecond paragraph.\n\n1. Session Title\n\n Session content here.\n\n";
let doc = parse_document(input).expect("Failed to parse");
assert_ast(&doc)
.item_count(2)
.item(0, |item| {
item.assert_paragraph()
.text("Second paragraph.")
.line_count(1);
})
.item(1, |item| {
item.assert_session()
.label("1. Session Title")
.child_count(1)
.child(0, |child| {
child
.assert_paragraph()
.text("Session content here.")
.line_count(1);
});
});
}
#[test]
fn test_dialog_parsing() {
let source = Lexplore::paragraph(9).source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item_count(1).item(0, |item| {
item.assert_paragraph()
.text("- Hi mom!!.\n- Hi kiddo.")
.line_count(2);
});
}
#[test]
fn test_trifecta_000_paragraphs() {
let source = Lexplore::trifecta(0).source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item_count(6);
assert_ast(&doc).item(0, |item| {
item.assert_paragraph()
.text("This is a simple paragraph with just one line. {{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(1, |item| {
item.assert_paragraph()
.text_contains("multi-line paragraph")
.text_contains("second line")
.text_contains("third line")
.text_contains("{{paragraph}}")
.line_count(3);
});
assert_ast(&doc).item(2, |item| {
item.assert_paragraph()
.text("Another paragraph follows after a blank line. {{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(3, |item| {
item.assert_paragraph()
.text_contains("special characters")
.text_contains("!@#$%^&*()_+-=[]{}|;':\",./<>?")
.text_contains("{{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(4, |item| {
item.assert_paragraph()
.text_contains("numbers")
.text_contains("123")
.text_contains("456")
.text_contains("789")
.text_contains("{{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(5, |item| {
item.assert_paragraph()
.text_contains("mixed content")
.text_contains("quick brown fox")
.text_contains("123 ABC def!")
.text_contains("{{paragraph}}")
.line_count(1);
});
}
#[test]
fn test_trifecta_010_paragraphs_sessions_flat_single() {
let source = Lexplore::trifecta(10).source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item_count(5);
assert_ast(&doc).item(0, |item| {
item.assert_paragraph()
.text_contains("combination of paragraphs and a single session")
.text_contains("{{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(1, |item| {
item.assert_session()
.label("1. Introduction {{session-title}}")
.child_count(2)
.child(0, |child| {
child
.assert_paragraph()
.text_contains("content of the session")
.text_contains("indented relative to the session title")
.text_contains("{{paragraph}}")
.line_count(1);
})
.child(1, |child| {
child
.assert_paragraph()
.text_contains("multiple paragraphs")
.text_contains("properly indented")
.text_contains("{{paragraph}}")
.line_count(1);
});
});
assert_ast(&doc).item(2, |item| {
item.assert_paragraph()
.text_contains("comes after the session")
.text_contains("root level")
.text_contains("{{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(3, |item| {
item.assert_session()
.label("Another Session {{session-title}}")
.child_count(1)
.child(0, |child| {
child
.assert_paragraph()
.text_contains("multiple sessions at the same level")
.text_contains("{{paragraph}}")
.line_count(1);
});
});
assert_ast(&doc).item(4, |item| {
item.assert_paragraph()
.text("Final paragraph at the root level. {{paragraph}}")
.line_count(1);
});
}
#[test]
fn test_trifecta_020_paragraphs_sessions_flat_multiple() {
let source = Lexplore::trifecta(20).source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item_count(8);
assert_ast(&doc).item(0, |item| {
item.assert_paragraph()
.text_contains("multiple sessions at the root level")
.text_contains("{{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(1, |item| {
item.assert_session()
.label("1. First Session {{session-title}}")
.child_count(2)
.child(0, |child| {
child
.assert_paragraph()
.text("This is the content of the first session. {{paragraph}}")
.line_count(1);
})
.child(1, |child| {
child
.assert_paragraph()
.text("It can have multiple paragraphs. {{paragraph}}")
.line_count(1);
});
});
assert_ast(&doc).item(2, |item| {
item.assert_session()
.label("2. Second Session {{session-title}}")
.child_count(1)
.child(0, |child| {
child
.assert_paragraph()
.text("The second session also has content. {{paragraph}}")
.line_count(1);
});
});
assert_ast(&doc).item(3, |item| {
item.assert_paragraph()
.text("A paragraph between sessions. {{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(4, |item| {
item.assert_session()
.label("3. Third Session {{session-title}}")
.child_count(1)
.child(0, |child| {
child
.assert_paragraph()
.text_contains("different amounts of content")
.text_contains("{{paragraph}}")
.line_count(1);
});
});
assert_ast(&doc).item(5, |item| {
item.assert_paragraph()
.text("Another paragraph. {{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(6, |item| {
item.assert_session()
.label("4. Session Without Numbering {{session-title}}")
.child_count(1) .child(0, |child| {
child
.assert_session()
.label("Session titles don't require numbering markers. {{session-title}}")
.child_count(1)
.child(0, |para| {
para.assert_paragraph()
.text_contains("They just need to be followed by a blank line")
.text_contains("{{paragraph}}")
.line_count(1);
});
});
});
assert_ast(&doc).item(7, |item| {
item.assert_paragraph()
.text("Final paragraph at the root level. {{paragraph}}")
.line_count(1);
});
}
#[test]
fn test_trifecta_030_sessions_nested_multiple() {
let source = Lexplore::trifecta(30).source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item_count(4);
assert_ast(&doc).item(0, |item| {
item.assert_paragraph()
.text_contains("sessions with nesting at various levels")
.text_contains("{{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(1, |item| {
item.assert_session()
.label("1. Root Session {{session-title}}")
.child_count(4); });
assert_ast(&doc).item(1, |item| {
item.assert_session().child(0, |child| {
child
.assert_paragraph()
.text_contains("first nesting level")
.text_contains("{{paragraph}}")
.line_count(1);
});
});
assert_ast(&doc).item(1, |item| {
item.assert_session().child(1, |child| {
child
.assert_session()
.label("1.1. First Sub-session {{session-title}}")
.child_count(2) .child(0, |para| {
para.assert_paragraph()
.text_contains("second nesting level")
.text_contains("{{paragraph}}")
.line_count(1);
})
.child(1, |para| {
para.assert_paragraph()
.text("It can have multiple paragraphs. {{paragraph}}")
.line_count(1);
});
});
});
assert_ast(&doc).item(1, |item| {
item.assert_session().child(2, |child| {
child
.assert_session()
.label("1.2. Second Sub-session {{session-title}}")
.child_count(2) .child(0, |para| {
para.assert_paragraph()
.text_contains("Another sub-session at the same level")
.text_contains("{{paragraph}}")
.line_count(1);
})
.child(1, |nested| {
nested
.assert_session()
.label("1.2.1. Deeply Nested Session {{session-title}}")
.child_count(2)
.child(0, |para| {
para.assert_paragraph()
.text_contains("third nesting level")
.text_contains("{{paragraph}}")
.line_count(1);
})
.child(1, |para| {
para.assert_paragraph()
.text_contains("nested arbitrarily deep")
.text_contains("{{paragraph}}")
.line_count(1);
});
});
});
});
assert_ast(&doc).item(1, |item| {
item.assert_session().child(3, |child| {
child
.assert_paragraph()
.text("Back to the first nesting level. {{paragraph}}")
.line_count(1);
});
});
assert_ast(&doc).item(2, |item| {
item.assert_session()
.label("2. Another Root Session {{session-title}}")
.child_count(2); });
assert_ast(&doc).item(2, |item| {
item.assert_session()
.child(0, |para| {
para.assert_paragraph()
.text_contains("root level alongside the first one")
.text_contains("{{paragraph}}")
.line_count(1);
})
.child(1, |subsession| {
subsession
.assert_session()
.label("2.1. Its Sub-session {{session-title}}")
.child_count(1)
.child(0, |para| {
para.assert_paragraph()
.text_contains("different numbering schemes")
.text_contains("{{paragraph}}")
.line_count(1);
});
});
});
assert_ast(&doc).item(3, |item| {
item.assert_paragraph()
.text("Final paragraph at the root level. {{paragraph}}")
.line_count(1);
});
}
#[test]
fn test_trifecta_040_lists() {
let source = Lexplore::trifecta(40).source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item_count(15);
assert_ast(&doc).item(0, |item| {
item.assert_paragraph()
.text_contains("various list formats and decorations")
.text_contains("{{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(1, |item| {
item.assert_paragraph()
.text("Plain dash lists: {{paragraph}}");
});
assert_ast(&doc).item(2, |item| {
item.assert_list().item_count(3);
});
assert_ast(&doc).item(3, |item| {
item.assert_paragraph()
.text("Numerical lists: {{paragraph}}");
});
assert_ast(&doc).item(4, |item| {
item.assert_list().item_count(3);
});
assert_ast(&doc).item(5, |item| {
item.assert_paragraph()
.text("Alphabetical lists: {{paragraph}}");
});
assert_ast(&doc).item(6, |item| {
item.assert_list().item_count(3);
});
assert_ast(&doc).item(7, |item| {
item.assert_paragraph()
.text_contains("Mixed decoration lists")
.text_contains("{{paragraph}}");
});
assert_ast(&doc).item(8, |item| {
item.assert_list().item_count(3);
});
assert_ast(&doc).item(9, |item| {
item.assert_paragraph()
.text("Parenthetical numbering: {{paragraph}}");
});
assert_ast(&doc).item(10, |item| {
item.assert_list().item_count(3);
});
assert_ast(&doc).item(11, |item| {
item.assert_paragraph()
.text("Roman numerals: {{paragraph}}");
});
assert_ast(&doc).item(12, |item| {
item.assert_list().item_count(3);
});
assert_ast(&doc).item(13, |item| {
item.assert_paragraph()
.text("Lists with longer content: {{paragraph}}");
});
assert_ast(&doc).item(14, |item| {
item.assert_list().item_count(3);
});
}
#[test]
fn test_trifecta_050_paragraph_lists() {
let source = Lexplore::trifecta(50).source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item_count(24);
assert_ast(&doc).item(0, |item| {
item.assert_paragraph()
.text_contains("disambiguation between paragraphs and lists")
.line_count(1);
});
assert_ast(&doc).item(1, |item| {
item.assert_paragraph()
.text_contains("Single item with dash")
.text_contains("- This is not a list")
.line_count(2);
});
assert_ast(&doc).item(2, |item| {
item.assert_paragraph()
.text_contains("Single item with number")
.text_contains("1. This is also not a list")
.line_count(2);
});
assert_ast(&doc).item(3, |item| {
item.assert_paragraph()
.text_contains("Lists require at least two items")
.line_count(1);
});
assert_ast(&doc).item(4, |item| {
item.assert_list().item_count(2);
});
assert_ast(&doc).item(5, |item| {
item.assert_paragraph()
.text_contains("Paragraph followed by list WITH blank line")
.line_count(1);
});
assert_ast(&doc).item(6, |item| {
item.assert_list().item_count(2);
});
assert_ast(&doc).item(7, |item| {
item.assert_paragraph()
.text_contains("List followed by paragraph without blank line")
.line_count(1);
});
assert_ast(&doc).item(8, |item| {
item.assert_list().item_count(2);
});
assert_ast(&doc).item(9, |item| {
item.assert_paragraph()
.text_contains("This paragraph follows after blank line")
.line_count(1);
});
assert_ast(&doc).item(10, |item| {
item.assert_paragraph()
.text_contains("Blank lines between list items")
.text_contains("- This is not")
.line_count(2);
});
assert_ast(&doc).item(11, |item| {
item.assert_paragraph()
.text("- A list {{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(12, |item| {
item.assert_paragraph()
.text_contains("Proper list with blank lines around it")
.line_count(1);
});
assert_ast(&doc).item(13, |item| {
item.assert_list().item_count(2);
});
assert_ast(&doc).item(14, |item| {
item.assert_paragraph()
.text("Paragraph after proper list. {{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(15, |item| {
item.assert_paragraph()
.text_contains("Valid mixed decoration list")
.line_count(1);
});
assert_ast(&doc).item(16, |item| {
item.assert_list().item_count(3);
});
assert_ast(&doc).item(17, |item| {
item.assert_paragraph()
.text_contains("List without preceding blank line")
.line_count(1);
});
assert_ast(&doc).item(18, |item| {
item.assert_list()
.item_count(2)
.item(0, |li| {
li.text_contains("No blank needed anymore");
})
.item(1, |li| {
li.text_contains("Parser stops paragraph before list start");
});
});
assert_ast(&doc).item(19, |item| {
item.assert_paragraph()
.text_contains("Paragraph resumes after list without blank")
.line_count(1);
});
assert_ast(&doc).item(20, |item| {
item.assert_paragraph()
.text_contains("Multiple paragraphs then list without blank")
.text_contains("Some more text in the same paragraph")
.line_count(2);
});
assert_ast(&doc).item(21, |item| {
item.assert_list()
.item_count(2)
.item(0, |li| {
li.text_contains("First no-blank item");
})
.item(1, |li| {
li.text_contains("Second no-blank item");
});
});
assert_ast(&doc).item(22, |item| {
item.assert_paragraph()
.text_contains("Back to paragraph after list")
.line_count(1);
});
assert_ast(&doc).item(23, |item| {
item.assert_paragraph()
.text_contains("Single list-like line stays in paragraph")
.text_contains("- This is not a list since only one item")
.text_contains("And this continues the same paragraph")
.line_count(3);
});
}
#[test]
fn test_trifecta_051_definitions_no_blank() {
let source = Lexplore::from_path(workspace_path(
"comms/specs/trifecta/051-definitions-no-blank.lex",
))
.source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item_count(8);
assert_ast(&doc).item(0, |item| {
item.assert_paragraph()
.text_contains("Paragraph before definition without blank line")
.line_count(1);
});
assert_ast(&doc).item(1, |item| {
item.assert_definition().subject("Subject");
});
assert_ast(&doc).item(2, |item| {
item.assert_paragraph()
.text_contains("Back to paragraph after definition")
.line_count(1);
});
assert_ast(&doc).item(3, |item| {
item.assert_paragraph()
.text_contains("Paragraph before definition with blank line")
.line_count(1);
});
assert_ast(&doc).item(4, |item| {
item.assert_definition().subject("Subject with blank");
});
assert_ast(&doc).item(5, |item| {
item.assert_paragraph()
.text_contains("Multi-line paragraph then definition")
.text_contains("More paragraph text here")
.line_count(2);
});
assert_ast(&doc).item(6, |item| {
item.assert_definition().subject("Another subject");
});
assert_ast(&doc).item(7, |item| {
item.assert_paragraph()
.text_contains("Final paragraph")
.line_count(1);
});
}
#[test]
fn test_trifecta_flat_simple() {
let source = Lexplore::from_path(workspace_path(
"comms/specs/trifecta/070-trifecta-flat-simple.lex",
))
.source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item(0, |item| {
item.assert_paragraph()
.text_contains("all three core elements");
});
assert_ast(&doc).item(1, |item| {
item.assert_session()
.label_contains("Session with Paragraph Content")
.child_count(2)
.child(0, |child| {
child
.assert_paragraph() .text_contains("starts with a paragraph");
})
.child(1, |child| {
child
.assert_paragraph() .text_contains("multiple paragraphs");
});
});
assert_ast(&doc).item(2, |item| {
item.assert_session()
.label_contains("Session with List Content")
.child_count(1)
.child(0, |child| {
child.assert_list().item_count(3);
});
});
assert_ast(&doc).item(3, |item| {
item.assert_session()
.label_contains("Session with Mixed Content")
.child_count(3)
.child(0, |child| {
child
.assert_paragraph()
.text_contains("starts with a paragraph");
})
.child(1, |child| {
child.assert_list().item_count(2);
})
.child(2, |child| {
child
.assert_paragraph()
.text_contains("ends with another paragraph");
});
});
assert_ast(&doc).item(4, |item| {
item.assert_paragraph().text_contains("root level");
});
assert_ast(&doc).item(5, |item| {
item.assert_list().item_count(2);
});
assert_ast(&doc).item(6, |item| {
item.assert_session()
.label_contains("Another Session")
.child_count(3)
.child(0, |child| {
child.assert_list().item_count(2);
})
.child(1, |child| {
child.assert_paragraph().text_contains("has a paragraph");
})
.child(2, |child| {
child.assert_list().item_count(2);
});
});
}
#[test]
fn test_trifecta_nesting() {
let source = Lexplore::trifecta(60).source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item(0, |item| {
item.assert_paragraph() .text_contains("various levels of nesting");
});
assert_ast(&doc).item(1, |item| {
item.assert_session()
.label_contains("1. Root Session")
.child_count(5); });
assert_ast(&doc).item(1, |item| {
item.assert_session().child(0, |child| {
child.assert_paragraph().text_contains("nested elements");
});
});
assert_ast(&doc).item(1, |item| {
item.assert_session().child(1, |child| {
child
.assert_session()
.label_contains("1.1. Sub-session")
.child_count(2) .child(0, |para| {
para.assert_paragraph();
})
.child(1, |list| {
list.assert_list().item_count(2);
});
});
});
assert_ast(&doc).item(1, |item| {
item.assert_session().child(2, |child| {
child
.assert_session()
.label_contains("1.2. Sub-session with List")
.child_count(3) .child(2, |nested| {
nested
.assert_session()
.label_contains("1.2.1. Deeply Nested")
.child_count(3); });
});
});
assert_ast(&doc).item(1, |item| {
item.assert_session().child(2, |subsession| {
subsession.assert_session().child(2, |deeply_nested| {
deeply_nested
.assert_session()
.child(1, |first_list| {
first_list.assert_list().item_count(2);
})
.child(2, |second_list| {
second_list.assert_list().item_count(2);
});
});
});
});
assert_ast(&doc).item(2, |item| {
item.assert_session()
.label_contains("2. Another Root Session")
.child_count(2); });
assert_ast(&doc).item(2, |item| {
item.assert_session().child(1, |subsession| {
subsession
.assert_session()
.label_contains("2.1. Mixed Content")
.child_count(4) .child(3, |deeply_nested| {
deeply_nested
.assert_session()
.label_contains("2.1.1. Even Deeper")
.child_count(4); });
});
});
assert_ast(&doc).item(3, |item| {
item.assert_paragraph()
.text_contains("Final root level paragraph");
});
}
#[test]
fn test_verified_ensemble_with_definitions() {
let source = Lexplore::definition(90).source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item(0, |item| {
item.assert_paragraph() .text_contains("all core elements");
});
assert_ast(&doc).item(1, |item| {
item.assert_definition()
.subject("Introduction")
.child_count(2)
.child(0, |child| {
child.assert_paragraph().text_contains("ensemble test");
})
.child(1, |child| {
child.assert_list().item_count(4);
});
});
assert_ast(&doc).item(2, |item| {
item.assert_session()
.label("1. Simple Elements Section {{session}}")
.child_count(5); });
assert_ast(&doc).item(3, |item| {
item.assert_session()
.label("2. Nested Elements Section {{session}}")
.child_count(3); });
}
#[test]
fn test_benchmark_010_kitchensink() {
let source = Lexplore::benchmark(10).source();
let doc = parse_document(&source).unwrap();
assert_ast(&doc).item_count(7);
assert_ast(&doc).item(0, |item| {
item.assert_paragraph()
.text_contains("*all major features*")
.text_contains("{{paragraph}}")
.line_count(1);
});
assert_ast(&doc).item(1, |item| {
item.assert_paragraph()
.text_contains("two-lined paragraph")
.text_contains("_definition_ at the root level")
.line_count(2);
});
assert_ast(&doc).item(2, |item| {
item.assert_definition()
.subject("Root Definition")
.child_count(2)
.child(0, |para| {
para.assert_paragraph()
.text_contains("contains a paragraph and a `list`")
.text_contains("{{definition}}")
.line_count(1);
})
.child(1, |list| {
list.assert_list().item_count(2);
});
});
assert_ast(&doc).item(3, |item| {
item.assert_paragraph()
.text_contains("marker annotation at the root level")
.line_count(1);
});
assert_ast(&doc).item(4, |item| {
item.assert_session()
.label("1. Primary Session {{session}}")
.child_count(5); });
assert_ast(&doc).item(4, |item| {
item.assert_session().child(0, |para| {
para.assert_paragraph()
.text_contains("main container for testing nested structures")
.text_contains("{{paragraph}}")
.line_count(1);
});
});
assert_ast(&doc).item(4, |item| {
item.assert_session().child(1, |list| {
list.assert_list().item_count(2);
});
});
assert_ast(&doc).item(4, |item| {
item.assert_session().child(1, |list| {
list.assert_list()
.annotation_count(1)
.annotation(0, |annotation| {
annotation.label("warning").child_count(1).child(0, |para| {
para.assert_paragraph()
.text_contains("single-line annotation inside the session");
});
});
});
});
assert_ast(&doc).item(4, |item| {
item.assert_session().child(2, |nested_session| {
nested_session
.assert_session()
.label("1.1. Nested Session (Level 2) {{session}}")
.child_count(3); });
});
assert_ast(&doc).item(4, |item| {
item.assert_session().child(2, |nested_session| {
nested_session.assert_session().child(0, |para| {
para.assert_paragraph()
.text_contains("second-level session")
.text_contains("{{paragraph}}")
.line_count(1);
});
});
});
assert_ast(&doc).item(4, |item| {
item.assert_session().child(2, |nested_session| {
nested_session.assert_session().child(1, |def| {
def.assert_definition()
.subject("Nested Definition")
.child_count(2) .child(0, |para| {
para.assert_paragraph()
.text_contains("inside a nested session")
.text_contains("{{definition}}")
.line_count(1);
})
.child(1, |list| {
list.assert_list().item_count(2);
});
});
});
});
assert_ast(&doc).item(4, |item| {
item.assert_session().child(2, |nested_session| {
nested_session.assert_session().child(2, |list| {
list.assert_list().item_count(2).item(0, |item| {
item.child_count(2)
.child(0, |para| {
para.assert_paragraph()
.text_contains("list item contains a nested paragraph")
.text_contains("{{paragraph}}")
.line_count(1);
})
.child(1, |nested_list| {
nested_list.assert_list().item_count(2);
});
});
});
});
});
assert_ast(&doc).item(4, |item| {
item.assert_session().child(3, |para| {
para.assert_paragraph()
.text_contains("paragraph back at the first level")
.text_contains("{{paragraph}}")
.line_count(1);
});
});
assert_ast(&doc).item(4, |item| {
item.assert_session().child(4, |verbatim| {
verbatim
.assert_verbatim_block()
.subject("Code Example (Verbatim Block)")
.line_count(4)
.content_contains("return \"lex\";");
});
});
assert_ast(&doc).item(5, |item| {
item.assert_session()
.label("2. Second Root Session {{session}}")
.child_count(2); });
assert_ast(&doc).item(5, |item| {
item.assert_session().child(0, |para| {
para.assert_paragraph()
.text_contains("annotations with block content")
.text_contains("{{paragraph}}")
.line_count(1);
});
});
assert_ast(&doc).item(5, |item| {
item.assert_session().child(1, |verbatim| {
verbatim
.assert_verbatim_block()
.annotation_count(1)
.annotation(0, |annotation| {
annotation
.label("todo")
.child_count(3)
.child(0, |para| {
para.assert_paragraph()
.text_contains("block annotation")
.text_contains("{{paragraph}}")
.line_count(1);
})
.child(1, |para| {
para.assert_paragraph()
.text_contains("contains a paragraph and a list")
.text_contains("{{paragraph}}")
.line_count(1);
})
.child(2, |list| {
list.assert_list().item_count(2);
});
});
});
});
assert_ast(&doc).item(5, |item| {
item.assert_session().child(1, |verbatim| {
verbatim
.assert_verbatim_block()
.subject("Image Reference (Marker Verbatim Block)")
.line_count(0); });
});
let description = doc
.root
.children
.iter()
.find_map(|item| match item {
ContentItem::Paragraph(para) if para.text().contains("*all major features*") => {
Some(para)
}
_ => None,
})
.expect("expected description paragraph");
let description_line = match description.lines.first() {
Some(ContentItem::TextLine(line)) => line,
_ => panic!("expected text line in description paragraph"),
};
InlineAssertion::new(&description_line.content, "kitchensink:description[0]").starts_with(&[
InlineExpectation::plain_text("This document includes "),
InlineExpectation::strong_text("all major features"),
InlineExpectation::plain(TextMatch::StartsWith(
" of the lex language to serve as a comprehensive \"kitchensink\"".into(),
)),
InlineExpectation::reference(ReferenceExpectation::citation_with_locator(
vec![TextMatch::Exact("spec2025".into())],
Some(TextMatch::Exact("pp. 45-46".into())),
)),
InlineExpectation::plain_text(". {{paragraph}}"),
]);
let root_definition = doc
.root
.children
.iter()
.find_map(|item| match item {
ContentItem::Definition(def) if def.subject.as_string().contains("Root Definition") => {
Some(def)
}
_ => None,
})
.expect("expected root definition");
let list = root_definition
.children
.iter()
.find_map(|child| match child {
ContentItem::List(list) => Some(list),
_ => None,
})
.expect("definition should contain a list");
let mut list_items = list.items.iter().filter_map(|item| match item {
ContentItem::ListItem(li) => Some(li),
_ => None,
});
let first_item = list_items.next().expect("missing first list item");
let first_text = first_item
.text
.first()
.expect("list item missing inline text content");
InlineAssertion::new(first_text, "kitchensink:definition:list[0]").starts_with(&[
InlineExpectation::plain_text("Item 1 in definition referencing "),
InlineExpectation::reference(ReferenceExpectation::tk(Some(TextMatch::Exact(
"rootlist".into(),
)))),
InlineExpectation::plain(TextMatch::StartsWith(". {{list-item}}".into())),
]);
let second_item = list_items.next().expect("missing second list item");
let second_text = second_item
.text
.first()
.expect("list item missing inline text content");
InlineAssertion::new(second_text, "kitchensink:definition:list[1]").starts_with(&[
InlineExpectation::plain_text("Item 2 in definition with note "),
InlineExpectation::reference(ReferenceExpectation::footnote_number(42)),
InlineExpectation::plain(TextMatch::StartsWith(". {{list-item}}".into())),
]);
assert_ast(&doc).item(6, |item| {
item.assert_paragraph()
.text("Final paragraph at the end of the document. {{paragraph}}")
.line_count(1);
});
}