krz/orgo
Lightning fast org-mode static site generator.
clone: git clone https://gitbay.org/krz/orgo.git
1//! Multi-file site build + new-construct snapshots for v0.2 (spec §5, Phase 4/5).
2//!
3//! Layers, per the spec's testing philosophy: rendered-HTML snapshots (resolved links,
4//! templated pages, tables, footnotes) plus explicit assertions that a cross-file link
5//! resolves to the right URL and that unresolved links are reported, not fatal.
6
7use camino::Utf8PathBuf;
8
9use org_ssg::index::{SymbolTable, TargetId};
10use org_ssg::parser::parse;
11use org_ssg::render::{render, Html, SyntectHighlighter};
12use org_ssg::resolve::{resolve, ResolvedDoc};
13use org_ssg::site::{render_site, BuiltPage};
14
15fn fixtures() -> Utf8PathBuf {
16 Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixtures")
17}
18
19fn build_fixture_site() -> Vec<BuiltPage> {
20 let (pages, broken) = render_site(&fixtures().join("site")).expect("build site");
21 assert!(broken.is_empty(), "fixture site has no broken links: {broken:?}");
22 pages
23}
24
25fn page<'a>(pages: &'a [BuiltPage], source: &str) -> &'a BuiltPage {
26 pages
27 .iter()
28 .find(|p| p.source == source)
29 .unwrap_or_else(|| panic!("no page for {source}"))
30}
31
32fn render_fragment(name: &str) -> String {
33 let path = fixtures().join(name);
34 let source = std::fs::read_to_string(&path).expect("read fixture");
35 let document = parse(Utf8PathBuf::from(name).as_path(), &source).expect("parse");
36 let Html(html) = render(&ResolvedDoc { document }, &SyntectHighlighter);
37 html
38}
39
40#[test]
41fn site_index_html() {
42 let pages = build_fixture_site();
43 insta::assert_snapshot!(page(&pages, "index.org").html);
44}
45
46#[test]
47fn site_guide_html() {
48 let pages = build_fixture_site();
49 insta::assert_snapshot!(page(&pages, "guide.org").html);
50}
51
52/// The invariant the whole RESOLVE stage exists for: a cross-file `[[#setup]]` link on
53/// the home page must resolve to the guide page's URL plus the target anchor.
54#[test]
55fn cross_file_link_resolves() {
56 let pages = build_fixture_site();
57 let index = &page(&pages, "index.org").html;
58 assert!(
59 index.contains("href=\"guide.html#setup\""),
60 "cross-file custom-id link should resolve to guide.html#setup, got:\n{index}"
61 );
62 // The `file:` link resolves to the bare output path.
63 assert!(
64 index.contains("href=\"guide.html\""),
65 "file: link should resolve to guide.html"
66 );
67 // A same-page `[[*Overview]]` link stays a local fragment.
68 assert!(
69 index.contains("href=\"#overview\""),
70 "same-page heading link should be a local fragment"
71 );
72}
73
74/// Unresolved internal links are reported as warnings, never a crash (spec §4.3.4).
75#[test]
76fn unresolved_link_is_reported_not_fatal() {
77 let source = "Broken [[#does-not-exist][link]] here.\n";
78 let doc = parse(Utf8PathBuf::from("orphan.org").as_path(), source).expect("parse");
79 let mut symbols = SymbolTable::new();
80 symbols.index_document(&doc);
81 let out = resolve(&doc, &symbols);
82 assert_eq!(out.broken.len(), 1, "one unresolved link expected");
83 assert_eq!(
84 out.broken[0].target,
85 TargetId::CustomId("does-not-exist".into())
86 );
87 assert!(out.used_targets.is_empty(), "nothing resolved, nothing used");
88}
89
90/// RESOLVE records the `uses` edges (spec §4.3, R2) even though incrementality does
91/// not consume them yet.
92#[test]
93fn resolve_records_used_targets() {
94 let pages = build_fixture_site();
95 // index.org uses: guide's #setup, guide.org (file), and its own *Overview → 3.
96 let _ = pages; // pages already assert no broken links; check the edge count directly.
97 let src = fixtures().join("site").join("index.org");
98 let source = std::fs::read_to_string(&src).unwrap();
99 let doc = parse(Utf8PathBuf::from("index.org").as_path(), &source).unwrap();
100
101 let guide_src = fixtures().join("site").join("guide.org");
102 let guide = parse(
103 Utf8PathBuf::from("guide.org").as_path(),
104 &std::fs::read_to_string(&guide_src).unwrap(),
105 )
106 .unwrap();
107
108 let mut symbols = SymbolTable::new();
109 symbols.index_document(&doc);
110 symbols.index_document(&guide);
111 let out = resolve(&doc, &symbols);
112 assert_eq!(out.used_targets.len(), 3, "three internal links resolved");
113 assert!(out.broken.is_empty());
114}
115
116#[test]
117fn table_render() {
118 insta::assert_snapshot!(render_fragment("table.org"));
119}
120
121#[test]
122fn footnote_render() {
123 insta::assert_snapshot!(render_fragment("footnote.org"));
124}