2026-05-12 15:10:27 +00:00
|
|
|
//! Cluster 1 of the parity-audit punch list: per-wiki config keys
|
|
|
|
|
//! (vimwiki globals shipped through `initializationOptions`) and the
|
|
|
|
|
//! HTML renderer's table-span attributes.
|
|
|
|
|
|
|
|
|
|
use nuwiki_core::ast::{
|
|
|
|
|
BlockNode, DocumentNode, InlineNode, ParagraphNode, Span, TableCellNode, TableNode,
|
|
|
|
|
TableRowNode, TextNode,
|
|
|
|
|
};
|
|
|
|
|
use nuwiki_core::render::{HtmlRenderer, Renderer};
|
|
|
|
|
use nuwiki_lsp::config::{config_from_json, WikiConfig};
|
|
|
|
|
|
|
|
|
|
// ===== Per-wiki config keys =====
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn defaults_carry_vimwiki_per_wiki_keys() {
|
|
|
|
|
let cfg = WikiConfig::empty();
|
|
|
|
|
assert_eq!(cfg.index, "index");
|
|
|
|
|
assert_eq!(cfg.diary_frequency, "daily");
|
|
|
|
|
assert_eq!(cfg.diary_start_week_day, "monday");
|
|
|
|
|
assert_eq!(cfg.diary_caption_level, 1);
|
|
|
|
|
assert_eq!(cfg.diary_sort, "desc");
|
|
|
|
|
assert_eq!(cfg.diary_header, "Diary");
|
|
|
|
|
assert_eq!(cfg.maxhi, 1);
|
|
|
|
|
assert_eq!(cfg.listsyms, " .oOX");
|
|
|
|
|
assert!(cfg.listsyms_propagate);
|
|
|
|
|
assert_eq!(cfg.list_margin, -1);
|
|
|
|
|
assert_eq!(cfg.links_space_char, " ");
|
|
|
|
|
assert!(cfg.nested_syntaxes.is_empty());
|
|
|
|
|
assert!(!cfg.auto_toc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn raw_wiki_parses_every_new_key() {
|
|
|
|
|
let cfg = config_from_json(serde_json::json!({
|
|
|
|
|
"wikis": [{
|
|
|
|
|
"root": "/tmp/w",
|
|
|
|
|
"index": "home",
|
|
|
|
|
"diary_frequency": "weekly",
|
|
|
|
|
"diary_start_week_day": "sunday",
|
|
|
|
|
"diary_caption_level": 2,
|
|
|
|
|
"diary_sort": "asc",
|
|
|
|
|
"diary_header": "Journal",
|
|
|
|
|
"maxhi": 6,
|
|
|
|
|
"listsyms": "abcde",
|
|
|
|
|
"listsyms_propagate": false,
|
|
|
|
|
"list_margin": 2,
|
|
|
|
|
"links_space_char": "_",
|
|
|
|
|
"nested_syntaxes": { "python": "python", "ruby": "ruby" },
|
|
|
|
|
"auto_toc": true,
|
|
|
|
|
}],
|
|
|
|
|
}));
|
|
|
|
|
let w = &cfg.wikis[0];
|
|
|
|
|
assert_eq!(w.index, "home");
|
|
|
|
|
assert_eq!(w.diary_frequency, "weekly");
|
|
|
|
|
assert_eq!(w.diary_start_week_day, "sunday");
|
|
|
|
|
assert_eq!(w.diary_caption_level, 2);
|
|
|
|
|
assert_eq!(w.diary_sort, "asc");
|
|
|
|
|
assert_eq!(w.diary_header, "Journal");
|
|
|
|
|
assert_eq!(w.maxhi, 6);
|
|
|
|
|
assert_eq!(w.listsyms, "abcde");
|
|
|
|
|
assert!(!w.listsyms_propagate);
|
|
|
|
|
assert_eq!(w.list_margin, 2);
|
|
|
|
|
assert_eq!(w.links_space_char, "_");
|
|
|
|
|
assert_eq!(
|
|
|
|
|
w.nested_syntaxes.get("python").map(String::as_str),
|
|
|
|
|
Some("python")
|
|
|
|
|
);
|
|
|
|
|
assert!(w.auto_toc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn legacy_single_wiki_shape_picks_up_defaults() {
|
|
|
|
|
let cfg = config_from_json(serde_json::json!({
|
|
|
|
|
"wiki_root": "/tmp/legacy",
|
|
|
|
|
}));
|
|
|
|
|
assert_eq!(cfg.wikis.len(), 1);
|
|
|
|
|
assert_eq!(cfg.wikis[0].index, "index");
|
|
|
|
|
assert_eq!(cfg.wikis[0].diary_frequency, "daily");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ===== Table colspan/rowspan =====
|
|
|
|
|
|
|
|
|
|
fn cell(text: &str, col_span: bool, row_span: bool) -> TableCellNode {
|
|
|
|
|
let s = Span::default();
|
|
|
|
|
TableCellNode {
|
|
|
|
|
span: s,
|
|
|
|
|
children: vec![InlineNode::Text(TextNode {
|
|
|
|
|
span: s,
|
|
|
|
|
content: text.into(),
|
|
|
|
|
})],
|
|
|
|
|
col_span,
|
|
|
|
|
row_span,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn table(rows: Vec<Vec<TableCellNode>>, has_header: bool) -> DocumentNode {
|
|
|
|
|
let span = Span::default();
|
|
|
|
|
let rows: Vec<TableRowNode> = rows
|
|
|
|
|
.into_iter()
|
|
|
|
|
.enumerate()
|
|
|
|
|
.map(|(i, cells)| TableRowNode {
|
|
|
|
|
span,
|
|
|
|
|
cells,
|
|
|
|
|
is_header: has_header && i == 0,
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
DocumentNode {
|
|
|
|
|
span,
|
|
|
|
|
metadata: Default::default(),
|
|
|
|
|
children: vec![BlockNode::Table(TableNode {
|
|
|
|
|
span,
|
|
|
|
|
rows,
|
|
|
|
|
has_header,
|
2026-05-12 21:45:43 +00:00
|
|
|
alignments: Vec::new(),
|
2026-05-12 15:10:27 +00:00
|
|
|
})],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn colspan_marker_folds_into_lead_cell() {
|
|
|
|
|
// | a | b | c |
|
|
|
|
|
// | d | > | e | → second row has b spanning 2 cols (cell index 1 is >)
|
|
|
|
|
let doc = table(
|
|
|
|
|
vec![
|
|
|
|
|
vec![
|
|
|
|
|
cell("a", false, false),
|
|
|
|
|
cell("b", false, false),
|
|
|
|
|
cell("c", false, false),
|
|
|
|
|
],
|
|
|
|
|
vec![
|
|
|
|
|
cell("d", false, false),
|
|
|
|
|
cell(">", true, false),
|
|
|
|
|
cell("e", false, false),
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
false,
|
|
|
|
|
);
|
|
|
|
|
let out = HtmlRenderer::new().render_to_string(&doc).unwrap();
|
|
|
|
|
assert!(out.contains(r#"<td colspan="2">d</td>"#), "got: {out}");
|
|
|
|
|
assert!(!out.contains(r#"class="col-span""#));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn rowspan_marker_folds_into_lead_cell_above() {
|
|
|
|
|
let doc = table(
|
|
|
|
|
vec![
|
|
|
|
|
vec![cell("a", false, false), cell("b", false, false)],
|
|
|
|
|
vec![cell("c", false, false), cell("\\/", false, true)],
|
|
|
|
|
],
|
|
|
|
|
false,
|
|
|
|
|
);
|
|
|
|
|
let out = HtmlRenderer::new().render_to_string(&doc).unwrap();
|
|
|
|
|
assert!(out.contains(r#"<td rowspan="2">b</td>"#), "got: {out}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn no_spans_emits_no_attrs() {
|
|
|
|
|
let doc = table(
|
|
|
|
|
vec![
|
|
|
|
|
vec![cell("a", false, false), cell("b", false, false)],
|
|
|
|
|
vec![cell("c", false, false), cell("d", false, false)],
|
|
|
|
|
],
|
|
|
|
|
false,
|
|
|
|
|
);
|
|
|
|
|
let out = HtmlRenderer::new().render_to_string(&doc).unwrap();
|
|
|
|
|
assert!(!out.contains("colspan="));
|
|
|
|
|
assert!(!out.contains("rowspan="));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure ParagraphNode is exercised so the import stays live.
|
|
|
|
|
#[test]
|
|
|
|
|
fn paragraph_render_is_unchanged() {
|
|
|
|
|
let p = DocumentNode {
|
|
|
|
|
span: Span::default(),
|
|
|
|
|
metadata: Default::default(),
|
|
|
|
|
children: vec![BlockNode::Paragraph(ParagraphNode {
|
|
|
|
|
span: Span::default(),
|
|
|
|
|
children: vec![InlineNode::Text(TextNode {
|
|
|
|
|
span: Span::default(),
|
|
|
|
|
content: "hi".into(),
|
|
|
|
|
})],
|
|
|
|
|
})],
|
|
|
|
|
};
|
|
|
|
|
let out = HtmlRenderer::new().render_to_string(&p).unwrap();
|
|
|
|
|
assert!(out.contains("hi"));
|
|
|
|
|
}
|