use accent_proust::ast::{ErrorLevel, Node, ValidationError};
use accent_proust::parse::{ParseOptions, PulldownTokenizer, parse_with};
fn table_syntax_errors<'n, 'a>(document: &'n Node<'a>) -> Vec<&'n ValidationError<'a>> {
let mut out: Vec<&ValidationError<'a>> = Vec::new();
let mut collect = |node: &'n Node<'a>| {
out.extend(
node.errors
.iter()
.filter(|error| error.id == "table-syntax"),
);
};
collect(document);
for node in document.walk() {
collect(node);
}
out
}
fn parse_document(source: &str) -> Node<'_> {
parse_with(
source,
&PulldownTokenizer::new(),
&ParseOptions::new().allow_comments(true),
)
}
fn start_line(error: &ValidationError<'_>) -> Option<usize> {
error.location.map(|location| location.start.line)
}
#[test]
fn produces_an_error_for_non_row_content_at_the_row_level_of_a_table() {
let input = "{% table %}\n\
* Heading 1\n\
* Heading 2\n\
---\n\
* Cell 1\n\
* Cell 2\n\
{% if $foo %}\n\
This is invalid conditional content at the row level of the table.\n\
{% /if %}\n\
This is invalid non-conditional content at the row level.\n\
{% /table %}";
let document = parse_document(input);
let errors = table_syntax_errors(&document);
assert_eq!(errors.len(), 2, "{errors:#?}");
for error in &errors {
assert_eq!(error.level, ErrorLevel::Critical);
assert!(error.message.contains("paragraph"), "{}", error.message);
assert!(error.message.contains("indented"), "{}", error.message);
}
let lines: Vec<Option<usize>> = errors.iter().map(|error| start_line(error)).collect();
assert!(
lines.contains(&Some(7)),
"no error on the conditional's paragraph: {lines:?}"
);
assert!(
lines.contains(&Some(9)),
"no error on the row-level paragraph: {lines:?}"
);
}
#[test]
fn does_not_produce_errors_for_valid_conditional_rows() {
let input = "{% table %}\n\
* Heading 1\n\
* Heading 2\n\
---\n\
{% if $foo %}\n\
* Row 1 Cell 1\n\
* Row 1 Cell 2\n\
{% /if %}\n\
---\n\
{% if $bar %}\n\
* Row 2 Cell 1\n\
* Row 2 Cell 2\n\
{% else /%}\n\
* Alt Row 2 Cell 1\n\
* Alt Row 2 Cell 2\n\
{% /if %}\n\
{% /table %}";
let document = parse_document(input);
assert!(table_syntax_errors(&document).is_empty());
}
#[test]
fn a_conditional_indented_within_a_cell_leaves_the_cell() {
let input = "{% table %}\n\
* Heading 1\n\
* Heading 2\n\
---\n\
* Cell 1\n\
* Cell 2\n\
\x20 {% if $foo %}\n\
\x20 This is a conditional paragraph inside cell 2.\n\
\x20 {% else /%}\n\
\x20 This is an alternate paragraph inside cell 2.\n\
\x20 {% /if %}\n\
{% /table %}";
let document = parse_document(input);
let errors = table_syntax_errors(&document);
assert_eq!(
errors.len(),
2,
"expected the two paragraphs of the escaped conditional to be rejected \
at the row level: {errors:#?}"
);
for error in &errors {
assert!(error.message.contains("paragraph"), "{}", error.message);
}
}
#[test]
fn does_not_produce_errors_for_a_conditional_with_multiple_rows_and_hr_separators() {
let input = "{% table %}\n\
* Heading 1\n\
* Heading 2\n\
---\n\
{% if $foo %}\n\
* Row 1 Cell 1\n\
* Row 1 Cell 2\n\
---\n\
* Row 2 Cell 1\n\
* Row 2 Cell 2\n\
{% /if %}\n\
{% /table %}";
let document = parse_document(input);
assert!(table_syntax_errors(&document).is_empty());
}
#[test]
fn does_not_produce_errors_for_comments_in_a_table() {
let input = "{% table %}\n\
* Heading 1\n\
* Heading 2\n\
---\n\
{% comment %}\n\
comment row\n\
{% /comment %}\n\
* Cell 1\n\
* Cell 2\n\
---\n\
{% if $foo %}\n\
{% comment %}\n\
comment inside conditional\n\
{% /comment %}\n\
* Row Cell 1\n\
* Row Cell 2\n\
{% /if %}\n\
{% /table %}";
let document = parse_document(input);
assert!(table_syntax_errors(&document).is_empty());
}
#[test]
fn produces_an_error_for_invalid_tags_inside_a_table_conditional() {
let input = "{% table %}\n\
* Heading 1\n\
* Heading 2\n\
---\n\
{% if $foo %}\n\
{% callout %}\n\
This is not a valid row\n\
{% /callout %}\n\
{% /if %}\n\
{% /table %}";
let document = parse_document(input);
let errors = table_syntax_errors(&document);
assert_eq!(errors.len(), 1, "{errors:#?}");
assert_eq!(errors[0].level, ErrorLevel::Critical);
assert!(
errors[0].message.contains("tag callout"),
"{}",
errors[0].message
);
assert_eq!(start_line(errors[0]), Some(5));
}
#[test]
fn produces_an_error_for_non_conditional_tags_at_the_row_level_of_a_table() {
let input = "{% table %}\n\
* Heading 1\n\
* Heading 2\n\
---\n\
{% callout %}\n\
This is not a valid row\n\
{% /callout %}\n\
{% /table %}";
let document = parse_document(input);
let errors = table_syntax_errors(&document);
assert_eq!(errors.len(), 1, "{errors:#?}");
assert_eq!(errors[0].level, ErrorLevel::Critical);
assert!(errors[0].message.contains("tag"), "{}", errors[0].message);
assert_eq!(start_line(errors[0]), Some(4));
}