@@ -1201,3 +1201,173 @@ let ``Paragraph between sublists should not be absorbed into first sublist item
12011201 paragraphPos |> should be ( greaterThan firstSublistEnd)
12021202 // Second sublist starts after paragraph.
12031203 secondSublistStart |> should be ( greaterThan paragraphPos)
1204+
1205+ // -----------------------------------------------------------------------
1206+ // ToMd serialisation tests
1207+ // These verify the round-trip Markdown-to-Markdown serialiser. Prior to
1208+ // this test file there was zero coverage of Markdown.ToMd.
1209+ // -----------------------------------------------------------------------
1210+
1211+ /// Round-trip helper: parse markdown, serialise back with ToMd (Unix newlines
1212+ /// so comparisons are platform-independent), then strip trailing whitespace.
1213+ let toMd ( input : string ) =
1214+ Markdown.Parse( input)
1215+ |> ( fun doc -> Markdown.ToMd( doc, newline = " \n " ))
1216+ |> ( fun s -> s.TrimEnd())
1217+
1218+ [<Test>]
1219+ let ``ToMd preserves a plain paragraph`` () =
1220+ " Hello, world." |> toMd |> should contain " Hello, world."
1221+
1222+ [<Test>]
1223+ let ``ToMd preserves a level - 1 heading`` () =
1224+ " # Heading One" |> toMd |> shouldEqual " # Heading One"
1225+
1226+ [<Test>]
1227+ let ``ToMd preserves a level - 2 heading`` () =
1228+ " ## Heading Two" |> toMd |> shouldEqual " ## Heading Two"
1229+
1230+ [<Test>]
1231+ let ``ToMd preserves a level - 3 heading`` () =
1232+ " ### Heading Three" |> toMd |> shouldEqual " ### Heading Three"
1233+
1234+ [<Test>]
1235+ let ``ToMd preserves strong ( bold ) text`` () =
1236+ " **bold**" |> toMd |> should contain " **bold**"
1237+
1238+ [<Test>]
1239+ let ``ToMd preserves inline code`` () =
1240+ " Use `printf` here." |> toMd |> should contain " `printf`"
1241+
1242+ [<Test>]
1243+ let ``ToMd preserves a direct link`` () =
1244+ " [FSharp](https://fsharp.org)"
1245+ |> toMd
1246+ |> should contain " [FSharp](https://fsharp.org)"
1247+
1248+ [<Test>]
1249+ let ``ToMd preserves a direct image`` () =
1250+ " " |> toMd |> should contain " "
1251+
1252+ [<Test>]
1253+ let ``ToMd preserves an unordered list`` () =
1254+ let md = " * apple\n * banana\n * cherry"
1255+ let result = toMd md
1256+ result |> should contain " apple"
1257+ result |> should contain " banana"
1258+ result |> should contain " cherry"
1259+
1260+ [<Test>]
1261+ let ``ToMd preserves emphasis ( italic ) text`` () =
1262+ // Emphasis must serialise as *...* not **...** (bold)
1263+ " *italic*" |> toMd |> should contain " *italic*"
1264+
1265+ [<Test>]
1266+ let ``ToMd preserves emphasis distinct from strong`` () =
1267+ let result = " **bold** and *italic*" |> toMd
1268+ result |> should contain " **bold**"
1269+ // Emphasis must not be rendered with double asterisks
1270+ result |> should not' ( contain " **italic**" )
1271+ result |> should contain " *italic*"
1272+
1273+ [<Test>]
1274+ let ``ToMd preserves an ordered list with correct numbering`` () =
1275+ let result = " 1. first\n 2. second\n 3. third" |> toMd
1276+ result |> should contain " 1. first"
1277+ result |> should contain " 2. second"
1278+ result |> should contain " 3. third"
1279+
1280+ [<Test>]
1281+ let ``ToMd ordered list does not use zero - based numbering`` () =
1282+ // Before fix: ordered list items were prefixed "0 ", "1 ", "2 " (0-indexed, no period)
1283+ let result = " 1. only item" |> toMd
1284+ result |> should not' ( contain " 0 only item" )
1285+ result |> should contain " 1. only item"
1286+
1287+ [<Test>]
1288+ let ``ToMd preserves a fenced code block`` () =
1289+ let md = " ```fsharp\n let x = 1\n ```"
1290+ let result = toMd md
1291+ result |> should contain " let x = 1"
1292+ result |> should contain " ```"
1293+
1294+ [<Test>]
1295+ let ``ToMd preserves a blockquote`` () =
1296+ let md = " > This is a quote."
1297+ let result = toMd md
1298+ result |> should contain " > "
1299+ result |> should contain " This is a quote."
1300+
1301+ [<Test>]
1302+ let ``ToMd preserves a horizontal rule`` () =
1303+ let md = " Before\n\n ---\n\n After"
1304+ let result = toMd md
1305+ result |> should contain " Before"
1306+ result |> should contain " ---"
1307+ result |> should contain " After"
1308+
1309+ [<Test>]
1310+ let ``ToMd preserves LaTeX inline math`` () =
1311+ let md = " Einstein's $E = mc^2$ equation."
1312+ let result = toMd md
1313+ result |> should contain " $E = mc^2$"
1314+
1315+ [<Test>]
1316+ let ``ToMd preserves inline HTML block`` () =
1317+ let md = " <div class=\" note\" >Note content</div>"
1318+ let result = toMd md
1319+ result |> should contain " <div"
1320+ result |> should contain " Note content"
1321+
1322+ [<Test>]
1323+ let ``ToMd handles a document with heading and paragraph`` () =
1324+ let md = " # Title\n\n Body text here."
1325+ let result = toMd md
1326+ result |> should contain " # Title"
1327+ result |> should contain " Body text here."
1328+
1329+ [<Test>]
1330+ let ``ToMd handles a table`` () =
1331+ let md = " Col1 | Col2\n :--- | :---\n A | B"
1332+ let result = toMd md
1333+ result |> should contain " Col1"
1334+ result |> should contain " Col2"
1335+ result |> should contain " A"
1336+ result |> should contain " B"
1337+
1338+ [<Test>]
1339+ let ``ToMd handles empty document`` () = " " |> toMd |> shouldEqual " "
1340+
1341+ [<Test>]
1342+ let ``ToMd preserves an indirect link when key is not resolved`` () =
1343+ // Indirect link whose reference definition is present — should round-trip
1344+ let md = " [link text][ref]\n\n [ref]: https://example.com"
1345+ let result = toMd md
1346+ // ToMd resolves the indirect link to a direct link form
1347+ result |> should contain " [link text]"
1348+ result |> should contain " https://example.com"
1349+
1350+ // --------------------------------------------------------------------------------------
1351+ // ToMd round-trip: indirect images (issue - failwith "tbd - IndirectImage")
1352+ // --------------------------------------------------------------------------------------
1353+
1354+ [<Test>]
1355+ let ``ToMd round - trip : indirect image with resolved reference`` () =
1356+ // Markdown with a reference-style image and a reference definition
1357+ let input = " ![alt text][img-id]\n\n [img-id]: http://example.com/image.png \" Title\" "
1358+ let doc = Markdown.Parse( input)
1359+ // Should NOT throw (was failing with failwith "tbd - IndirectImage")
1360+ let result = Markdown.ToMd( doc)
1361+ // When key is resolved, should render as a direct image
1362+ result |> should contain "  =
1367+ // Parse just the image token without a reference definition
1368+ let input = " ![alt text][unknown-ref]"
1369+ let doc = Markdown.Parse( input)
1370+ // Should NOT throw (was failing with failwith "tbd - IndirectImage")
1371+ let result = Markdown.ToMd( doc)
1372+ // When key is not resolved, should preserve the indirect form
1373+ result |> should contain " ![alt text][unknown-ref]"
0 commit comments