rumdl_lib/utils/
mkdocs_test_utils.rs1#[cfg(test)]
7#[derive(Debug)]
8pub struct PatternTestCase {
9 pub input: &'static str,
10 pub expected: bool,
11 pub description: &'static str,
12}
13
14#[cfg(test)]
16#[derive(Debug)]
17pub struct PositionTestCase {
18 pub content: &'static str,
19 pub test_positions: Vec<(&'static str, bool)>, pub description: &'static str,
21}
22
23#[cfg(test)]
25#[derive(Debug)]
26pub struct IndentTestCase {
27 pub input: &'static str,
28 pub expected: Option<usize>,
29 pub description: &'static str,
30}
31
32#[cfg(test)]
34pub fn run_pattern_tests<F>(test_fn: F, cases: &[PatternTestCase])
35where
36 F: Fn(&str) -> bool,
37{
38 for case in cases {
39 assert_eq!(
40 test_fn(case.input),
41 case.expected,
42 "Failed: {} - Input: {:?}",
43 case.description,
44 case.input
45 );
46 }
47}
48
49#[cfg(test)]
51pub fn run_position_tests<F>(test_fn: F, cases: &[PositionTestCase])
52where
53 F: Fn(&str, usize) -> bool,
54{
55 for case in cases {
56 for (substring, expected) in &case.test_positions {
57 let pos = case.content.find(substring).unwrap_or_else(|| {
58 panic!(
59 "Substring '{}' not found in content for test: {}",
60 substring, case.description
61 )
62 });
63 assert_eq!(
64 test_fn(case.content, pos),
65 *expected,
66 "Failed: {} - Position test for substring '{}' at position {}",
67 case.description,
68 substring,
69 pos
70 );
71 }
72 }
73}
74
75#[cfg(test)]
77pub fn run_indent_tests<F>(test_fn: F, cases: &[IndentTestCase])
78where
79 F: Fn(&str) -> Option<usize>,
80{
81 for case in cases {
82 assert_eq!(
83 test_fn(case.input),
84 case.expected,
85 "Failed: {} - Input: {:?}",
86 case.description,
87 case.input
88 );
89 }
90}
91
92#[cfg(test)]
94pub fn create_mkdocs_test_document() -> String {
95 r#"# Test Document
96
97Regular paragraph text.
98
99!!! note "Test Note"
100 This is an admonition with content.
101
102 Multiple lines of content.
103
104[^1]: This is a footnote definition
105 with multiple lines
106 of content.
107
108=== "Tab 1"
109
110 Content in tab 1.
111
112 More content.
113
114=== "Tab 2"
115
116 Content in tab 2.
117
118::: mymodule.MyClass
119 handler: python
120 options:
121 show_source: true
122
123--8<-- "included.md"
124
125Regular text with [^1] footnote reference.
126
127<!-- --8<-- [start:section] -->
128Section content
129<!-- --8<-- [end:section] -->
130
131Final paragraph."#
132 .to_string()
133}
134
135#[cfg(test)]
137pub fn assert_positions<F>(content: &str, test_fn: F, positions: &[(&str, bool)])
138where
139 F: Fn(&str, usize) -> bool,
140{
141 for (substring, expected) in positions {
142 if let Some(pos) = content.find(substring) {
143 let actual = test_fn(content, pos);
144 assert_eq!(
145 actual, *expected,
146 "Position test failed for '{substring}' at position {pos}. Expected: {expected}, Got: {actual}"
147 );
148 } else {
149 panic!("Substring '{substring}' not found in content");
150 }
151 }
152}
153
154#[cfg(test)]
156pub struct TestContentBuilder {
157 lines: Vec<String>,
158}
159
160#[cfg(test)]
161impl TestContentBuilder {
162 pub fn new() -> Self {
163 Self { lines: Vec::new() }
164 }
165
166 pub fn add_line(mut self, line: &str) -> Self {
167 self.lines.push(line.to_string());
168 self
169 }
170
171 pub fn add_empty_line(mut self) -> Self {
172 self.lines.push(String::new());
173 self
174 }
175
176 pub fn add_indented(mut self, indent: usize, content: &str) -> Self {
177 self.lines.push(format!("{}{}", " ".repeat(indent), content));
178 self
179 }
180
181 pub fn add_admonition(mut self, admon_type: &str, title: Option<&str>) -> Self {
182 let line = if let Some(t) = title {
183 format!("!!! {admon_type} \"{t}\"")
184 } else {
185 format!("!!! {admon_type}")
186 };
187 self.lines.push(line);
188 self
189 }
190
191 pub fn add_footnote_def(mut self, ref_name: &str, content: &str) -> Self {
192 self.lines.push(format!("[^{ref_name}]: {content}"));
193 self
194 }
195
196 pub fn add_tab(mut self, label: &str) -> Self {
197 self.lines.push(format!("=== \"{label}\""));
198 self
199 }
200
201 pub fn add_snippet(mut self, file: &str) -> Self {
202 self.lines.push(format!("--8<-- \"{file}\""));
203 self
204 }
205
206 pub fn build(self) -> String {
207 self.lines.join("\n")
208 }
209}
210
211#[cfg(test)]
212impl Default for TestContentBuilder {
213 fn default() -> Self {
214 Self::new()
215 }
216}