//! 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>, has_header: bool) -> DocumentNode { let span = Span::default(); let rows: Vec = 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, })], } } #[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#"d"#), "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#"b"#), "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")); }