use crate::tests::prelude::*;
track_file!("ref/asciidoc-lang/docs/modules/tables/pages/add-footer-row.adoc");
fn parse_table(source: &str) -> crate::blocks::TableBlock<'_> {
let mut parser = Parser::default();
let mi = crate::blocks::Block::parse(crate::Span::new(source), &mut parser)
.unwrap_if_no_warnings()
.unwrap();
match mi.item {
crate::blocks::Block::Table(table) => table,
other => panic!("expected a table block, got {other:?}"),
}
}
fn simple_text(cell: &crate::blocks::TableCell<'_>) -> String {
match cell.content() {
crate::blocks::TableCellContent::Simple(content) => content.rendered().to_string(),
crate::blocks::TableCellContent::AsciiDoc(_) => panic!("expected simple cell content"),
}
}
fn footer_texts(table: &crate::blocks::TableBlock<'_>) -> Vec<String> {
table
.footer_row()
.expect("expected a footer row")
.cells()
.iter()
.map(simple_text)
.collect()
}
non_normative!(
r#"
= Create a Footer Row
"#
);
#[test]
fn intro() {
verifies!(
r#"
The last row of a table is promoted to a footer row if the `footer` value is assigned to the table's `options` attribute.
"#
);
let table = parse_table("[options=\"footer\"]\n|===\n|Cell 1 |Cell 2\n|Foot 1 |Foot 2\n|===");
assert_eq!(footer_texts(&table), vec!["Foot 1", "Foot 2"]);
assert_eq!(table.body_rows().len(), 1);
let no_footer = parse_table("|===\n|Cell 1 |Cell 2\n|Foot 1 |Foot 2\n|===");
assert!(no_footer.footer_row().is_none());
}
#[test]
fn assign_footer_to_last_row() {
non_normative!(
r#"
== Assign footer to the last row
"#
);
verifies!(
r#"
The footer row semantics and styles are applied to the last row in a table by assigning `footer` to the `options` attribute.
The `options` attribute is set in the table's attribute list using the shorthand (`%value`) or formal syntax (`options="value"`).
"#
);
let shorthand = parse_table("[%footer]\n|===\n|Cell 1 |Cell 2\n|Foot 1 |Foot 2\n|===");
assert_eq!(footer_texts(&shorthand), vec!["Foot 1", "Foot 2"]);
let formal = parse_table("[options=\"footer\"]\n|===\n|Cell 1 |Cell 2\n|Foot 1 |Foot 2\n|===");
assert_eq!(footer_texts(&formal), vec!["Foot 1", "Foot 2"]);
let styled =
parse_table("[%footer,cols=\"a,1\"]\n|===\n|Cell 1 |Cell 2\n|* item\n|Foot 2\n|===");
assert!(matches!(
styled.footer_row().unwrap().cells()[0].content(),
crate::blocks::TableCellContent::AsciiDoc(_)
));
verifies!(
r#"
The `options` attribute is represented by the percent sign (`%`) when it's set using the shorthand syntax.
"#
);
let table = parse_table("[%footer]\n|===\n|Cell 1 |Cell 2\n|Foot 1 |Foot 2\n|===");
assert!(table.footer_row().is_some());
verifies!(
r#"
In <<ex-short>>, `footer` is assigned using the shorthand syntax for `options`.
.Table with footer assigned using the shorthand syntax
[source#ex-short]
----
[%header%footer,cols="2,2,1"] <.>
|===
|Column 1, header row
|Column 2, header row
|Column 3, header row
|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|Column 1, footer row
|Column 2, footer row
|Column 3, footer row
|===
----
"#
);
let ex_short = parse_table(
"[%header%footer,cols=\"2,2,1\"]\n|===\n|Column 1, header row\n|Column 2, header row\n|Column 3, header row\n\n|Cell in column 1, row 2\n|Cell in column 2, row 2\n|Cell in column 3, row 2\n\n|Column 1, footer row\n|Column 2, footer row\n|Column 3, footer row\n|===",
);
assert!(ex_short.footer_row().is_some());
verifies!(
r#"
<.> Values assigned using the shorthand syntax must be entered before the `cols` attribute (or any other named attributes) in a table's attribute list, otherwise the processor will ignore them.
"#
);
let before = parse_table(
"[%header%footer,cols=\"2,2,1\"]\n|===\n|Column 1, header row\n|Column 2, header row\n|Column 3, header row\n\n|Cell in column 1, row 2\n|Cell in column 2, row 2\n|Cell in column 3, row 2\n\n|Column 1, footer row\n|Column 2, footer row\n|Column 3, footer row\n|===",
);
assert!(before.header_row().is_some());
assert_eq!(
footer_texts(&before),
vec![
"Column 1, footer row",
"Column 2, footer row",
"Column 3, footer row"
]
);
assert_eq!(before.body_rows().len(), 1);
let after = parse_table(
"[cols=\"2,2,1\",%footer]\n|===\n|Cell 1 |Cell 2 |Cell 3\n\n|Foot 1 |Foot 2 |Foot 3\n|===",
);
assert!(after.footer_row().is_none());
verifies!(
r#"
The table from <<ex-short>> is displayed below.
.Result of <<ex-short>>
[%header%footer,cols="2,2,1"]
|===
|Column 1, header row
|Column 2, header row
|Column 3, header row
|Cell in column 1, row 2
|Cell in column 2, row 2
|Cell in column 3, row 2
|Column 1, footer row
|Column 2, footer row
|Column 3, footer row
|===
"#
);
let ex_short_result = parse_table(
"[%header%footer,cols=\"2,2,1\"]\n|===\n|Column 1, header row\n|Column 2, header row\n|Column 3, header row\n\n|Cell in column 1, row 2\n|Cell in column 2, row 2\n|Cell in column 3, row 2\n\n|Column 1, footer row\n|Column 2, footer row\n|Column 3, footer row\n|===",
);
assert_eq!(
footer_texts(&ex_short_result),
vec![
"Column 1, footer row",
"Column 2, footer row",
"Column 3, footer row"
]
);
verifies!(
r#"
In <<ex-formal>>, the `options` attribute is set and assigned the `footer` value using the formal syntax.
The `options` attribute accepts a comma-separated list of values.
.Table with footer assigned to the options attribute
[source#ex-formal]
----
include::example$row.adoc[tag=opt-f]
----
"#
);
let ex_formal =
parse_table("[options=\"footer\"]\n|===\n|Cell 1 |Cell 2\n|Foot 1 |Foot 2\n|===");
assert!(ex_formal.footer_row().is_some());
let table = parse_table(
"[options=\"footer,unbreakable\"]\n|===\n|Cell 1 |Cell 2\n|Foot 1 |Foot 2\n|===",
);
assert!(table.footer_row().is_some());
verifies!(
r#"
The last row of the table in <<ex-formal>> is rendered using the corresponding footer styles.
.Result of <<ex-formal>>
include::example$row.adoc[tag=opt-f]
"#
);
let formal = parse_table(
"[options=\"footer\"]\n|===\n|Column 1, header row |Column 2, header row\n\n|Cell in column 1, row 2\n|Cell in column 2, row 2\n\n|Column 1, footer row\n|Column 2, footer row\n|===",
);
assert!(formal.header_row().is_some());
assert_eq!(formal.body_rows().len(), 1);
assert_eq!(
footer_texts(&formal),
vec!["Column 1, footer row", "Column 2, footer row"]
);
}