1use markodown::{transform, ComponentImports, OutputFormat};
2use std::fs;
3use std::path::Path;
4
5/// Load a fixture pair and test the transform.
6///
7/// Each fixture has an `.mdo` input and either an `.html` expected output
8/// (static mode) or a `.marko` expected output (marko mode), or both.
9fn run_fixture(name: &str) {
10 run_fixture_with_options(name, None, None, None);
11}
12
13/// Run fixture with optional layout, component_imports, and self_path arguments.
14fn run_fixture_with_options(
15 name: &str,
16 layout: Option<&str>,
17 component_imports: Option<ComponentImports>,
18 self_path: Option<&str>,
19) {
20 let base = Path::new("tests/fixtures").join(name);
21 let input_path = base.with_extension("mdo");
22 let html_path = base.with_extension("html");
23 let marko_path = base.with_extension("marko");
24
25 let source = fs::read_to_string(&input_path)
26 .unwrap_or_else(|e| panic!("failed to read {}: {e}", input_path.display()));
27
28 // Test HTML output if expected file exists
29 if html_path.exists() {
30 let expected = fs::read_to_string(&html_path)
31 .unwrap_or_else(|e| panic!("failed to read {}: {e}", html_path.display()));
32
33 let output = transform(
34 &source,
35 Some(OutputFormat::Html),
36 layout.map(|s| s.to_string()),
37 component_imports.clone(),
38 self_path.map(|s| s.to_string()),
39 false,
40 None,
41 )
42 .unwrap();
43
44 assert_eq!(
45 output.format,
46 OutputFormat::Html,
47 "[{name}] expected Html output but got Marko"
48 );
49
50 assert_eq!(
51 output.text.trim(),
52 expected.trim(),
53 "\n\n[{name}] HTML output mismatch\n\n--- expected ---\n{expected}\n--- actual ---\n{}\n",
54 output.text
55 );
56 }
57 // Test Marko output if expected file exists
58 else if marko_path.exists() {
59 let expected = fs::read_to_string(&marko_path)
60 .unwrap_or_else(|e| panic!("failed to read {}: {e}", marko_path.display()));
61
62 let output = transform(
63 &source,
64 Some(OutputFormat::Marko),
65 layout.map(|s| s.to_string()),
66 component_imports.clone(),
67 self_path.map(|s| s.to_string()),
68 false,
69 None,
70 )
71 .unwrap();
72
73 assert_eq!(
74 output.format,
75 OutputFormat::Marko,
76 "[{name}] expected Marko output but got Html"
77 );
78
79 assert_eq!(
80 output.text.trim(),
81 expected.trim(),
82 "\n\n[{name}] Marko output mismatch\n\n--- expected ---\n{expected}\n--- actual ---\n{}\n",
83 output.text
84 );
85 } else {
86 panic!("no test defined")
87 }
88}
89
90#[test]
91fn fixture_01_static_markdown() {
92 run_fixture("01-static-markdown");
93}
94
95// The tests below have .marko expected outputs. They will be validated
96// once the marko renderer and custom parse rules are implemented.
97// For now they just verify the transform doesn't panic.
98
99#[test]
100fn fixture_02_imports_and_components() {
101 run_fixture("02-imports-and-components");
102}
103
104#[test]
105fn fixture_03_static_and_expressions() {
106 run_fixture("03-static-and-expressions");
107}
108
109#[test]
110fn fixture_04_control_flow() {
111 run_fixture("04-control-flow");
112}
113
114#[test]
115fn fixture_05_nested_tags_and_loops() {
116 run_fixture("05-nested-tags-and-loops");
117}
118
119#[test]
120fn fixture_06_comments() {
121 run_fixture("06-comments");
122}
123
124#[test]
125fn fixture_07_server_client() {
126 run_fixture("07-server-client");
127}
128
129#[test]
130fn fixture_08_inline_tags() {
131 run_fixture("08-inline-tags");
132}
133
134#[test]
135fn fixture_09_marko_shorthands() {
136 run_fixture("09-marko-shorthands");
137}
138
139#[test]
140fn fixture_10_attribute_tags() {
141 run_fixture("10-attribute-tags");
142}
143
144#[test]
145fn fixture_11_autolinks() {
146 run_fixture("11-autolinks");
147}
148
149#[test]
150fn fixture_12_frontmatter() {
151 run_fixture("12-frontmatter");
152}
153
154#[test]
155fn fixture_13_kitchen_sink() {
156 run_fixture("13-kitchen-sink");
157}
158
159#[test]
160fn fixture_14_evil_braces_and_typescript() {
161 run_fixture("14-evil-braces-and-typescript");
162}
163
164#[test]
165fn fixture_15_multiline_tag() {
166 run_fixture("15-multiline-tag");
167}
168
169#[test]
170fn fixture_16_same_line_text() {
171 run_fixture("16-same-line-text");
172}
173
174#[test]
175fn fixture_17_consecutive_statements() {
176 run_fixture("17-consecutive-statements");
177}
178
179#[test]
180fn fixture_18_multiline_tag_with_value() {
181 run_fixture("18-multiline-tag-with-value");
182}
183
184#[test]
185fn fixture_19_extra_cases() {
186 run_fixture("19-extra-cases");
187}
188
189#[test]
190fn fixture_20_frontmatter_preamble() {
191 run_fixture("20-frontmatter-preamble");
192}
193
194#[test]
195fn fixture_21_frontmatter_blank_lines() {
196 run_fixture("21-frontmatter-blank-lines");
197}
198
199#[test]
200fn fixture_22_frontmatter_mixed_preamble() {
201 run_fixture("22-frontmatter-mixed-preamble");
202}
203
204#[test]
205fn fixture_23_outline_extracting() {
206 run_fixture_with_options("23-outline-extracting", Some("./layout.marko"), None, None);
207}
208
209#[test]
210fn fixture_24_heading_import() {
211 run_fixture_with_options(
212 "24-heading-import",
213 None,
214 Some(ComponentImports {
215 heading: Some("./heading.marko".to_string()),
216 ..Default::default()
217 }),
218 None,
219 );
220}
221
222#[test]
223fn fixture_25_code_block_import() {
224 run_fixture_with_options(
225 "25-code-block-import",
226 None,
227 Some(ComponentImports {
228 code_block: Some("./code-block.marko".to_string()),
229 ..Default::default()
230 }),
231 None,
232 );
233}
234
235#[test]
236fn fixture_26_code_block_import() {
237 run_fixture_with_options("26-code-block-import", None, None, None);
238}
239
240#[test]
241fn fixture_27_frontmatter_layout() {
242 // layout key in frontmatter activates layout wrapping without a layoutImport option
243 run_fixture("27-frontmatter-layout");
244}
245
246#[test]
247fn fixture_28_outline_self_import() {
248 // layout + selfImport produces module=self__markodown__ and the extra import
249 run_fixture_with_options(
250 "28-outline-self-import",
251 Some("./layout.marko"),
252 None,
253 Some("./self.marko"),
254 );
255}
256
257#[test]
258fn fixture_29_layout_all_components() {
259 // layout emits boilerplate for all element types present in the document
260 run_fixture_with_options(
261 "29-layout-all-components",
262 Some("./layout.marko"),
263 None,
264 None,
265 );
266}
267
268#[test]
269fn fixture_30_layout_selective_components() {
270 // only element types actually used in the document get boilerplate — others are omitted
271 run_fixture_with_options(
272 "30-layout-selective-components",
273 Some("./layout.marko"),
274 None,
275 None,
276 );
277}
278
279#[test]
280fn fixture_31_import_with_markdown_content() {
281 // import statement followed (after blank line) by Marko tags with Markdown inline content
282 // regression: statement rule was passing unbounded source to OXC which tried to parse
283 // markdown link syntax [text](url) as a TypeScript array expression
284 run_fixture("31-import-with-markdown-content");
285}
286
287#[test]
288fn fixture_32_inline_tag_in_paragraph() {
289 // regression: a self-contained inline tag (<b>text</b>) on its own line within a paragraph
290 // was being consumed by the block tag rule, splitting one paragraph into three
291 run_fixture("32-inline-tag-in-paragraph");
292}