From 831a61865a24577fc51ae631af0230f7be6f7e49 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 18 May 2026 18:28:15 +0200 Subject: [PATCH 1/3] Fix ShouldContainHtml comparing actual HTML to itself expectedCompare was built from `actual` instead of `expected`, causing the assertion to always pass regardless of whether the expected fragment was present in the actual output. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../PrettyHtmlExtensions.cs | 2 +- .../PrettyHtmlExtensionsTests.cs | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/Elastic.Markdown.Tests/PrettyHtmlExtensionsTests.cs diff --git a/tests/Elastic.Markdown.Tests/PrettyHtmlExtensions.cs b/tests/Elastic.Markdown.Tests/PrettyHtmlExtensions.cs index 221abb89c7..b24e21482d 100644 --- a/tests/Elastic.Markdown.Tests/PrettyHtmlExtensions.cs +++ b/tests/Elastic.Markdown.Tests/PrettyHtmlExtensions.cs @@ -81,7 +81,7 @@ public static void ShouldContainHtml( actual = actual.Trim('\n').PrettyHtml(sanitize); var actualCompare = actual.Replace("\t", string.Empty); - var expectedCompare = actual.Replace("\t", string.Empty); + var expectedCompare = expected.Replace("\t", string.Empty); // we compare over unindented HTML, but if that fails, we rely on the pretty HTML Contain(). // to throw for improved error messages diff --git a/tests/Elastic.Markdown.Tests/PrettyHtmlExtensionsTests.cs b/tests/Elastic.Markdown.Tests/PrettyHtmlExtensionsTests.cs new file mode 100644 index 0000000000..ee5606bf8d --- /dev/null +++ b/tests/Elastic.Markdown.Tests/PrettyHtmlExtensionsTests.cs @@ -0,0 +1,27 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using AwesomeAssertions; +using Xunit.Sdk; + +namespace Elastic.Markdown.Tests; + +public class PrettyHtmlExtensionsTests +{ + [Fact] + public void ShouldContainHtml_WhenExpectedHtmlIsMissing_Throws() + { + var actual = """ +

Rendered output

+ """; + + var expected = """ + Missing output + """; + + var act = () => actual.ShouldContainHtml(expected); + + act.Should().Throw(); + } +} From 0b798d8757fb15687cb6ccd1f55552c7549d309f Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 18 May 2026 18:52:32 +0200 Subject: [PATCH 2/3] Fix inline image renderer emitting alt text as title unconditionally The renderer was always writing title="" for every image, ignoring the actual title parsed from the markdown title syntax. This caused all images to get a spurious title attribute, and made it impossible for explicit titles from "My Title =50%" syntax to appear correctly. Use link.Title (already correctly set by ParseStylingInstructions) and only emit the title attribute when it is non-empty. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../Myst/Renderers/HtmxLinkInlineRenderer.cs | 11 +++++------ .../Elastic.Markdown.Tests/Inline/SubstitutionTest.cs | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs b/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs index a33e8de5d9..1846f268f7 100644 --- a/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs +++ b/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs @@ -91,17 +91,16 @@ private static void WriteImage(HtmlRenderer renderer, LinkInline link) _ = renderer.Write('"'); } - // Write any additional attributes (like width/height from styling instructions) - _ = renderer.WriteAttributes(link); - - // Set title to alt text for inline images (after any substitutions are processed) - if (link.FirstChild != null) + if (!string.IsNullOrEmpty(link.Title)) { _ = renderer.Write(" title=\""); - renderer.WriteChildren(link); + _ = renderer.WriteEscape(link.Title); _ = renderer.Write('"'); } + // Write any additional attributes (like width/height from styling instructions) + _ = renderer.WriteAttributes(link); + _ = renderer.Write(" />"); } diff --git a/tests/Elastic.Markdown.Tests/Inline/SubstitutionTest.cs b/tests/Elastic.Markdown.Tests/Inline/SubstitutionTest.cs index 60c53be3d0..177e303e74 100644 --- a/tests/Elastic.Markdown.Tests/Inline/SubstitutionTest.cs +++ b/tests/Elastic.Markdown.Tests/Inline/SubstitutionTest.cs @@ -203,7 +203,7 @@ public class ReplaceInImageTitle(ITestOutputHelper output) : InlineTest(output, [Fact] public void OnlySeesGlobalVariable() => Html.Should().NotContain("title=\"{{hello-world}}\"") - .And.Contain("title=\"Observability\""); + .And.Contain("title=\"Hello World\""); } public class MutationOperatorTest(ITestOutputHelper output) : InlineTest(output, From e74861fad13ed4dd78f37ff249338c041eeff4ae Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 18 May 2026 19:08:50 +0200 Subject: [PATCH 3/3] Align C# inline image tests with authoring test ground truth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The F# authoring tests (the ground truth) document that inline images always use alt text as the title attribute — even when an explicit title is supplied in the markdown title syntax. The renderer behaviour was correct; the C# xUnit expectations were wrong (missing title, or using the parsed title instead of alt text) and had been silently passing because of the ShouldContainHtml bug fixed in the previous commit. Restore the renderer to always write title from alt text and update the C# tests to match. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../Myst/Renderers/HtmxLinkInlineRenderer.cs | 11 ++++++----- .../Inline/InlineImageTest.cs | 16 ++++++++-------- .../Inline/SubstitutionTest.cs | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs b/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs index 1846f268f7..873ae1cd2b 100644 --- a/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs +++ b/src/Elastic.Markdown/Myst/Renderers/HtmxLinkInlineRenderer.cs @@ -91,16 +91,17 @@ private static void WriteImage(HtmlRenderer renderer, LinkInline link) _ = renderer.Write('"'); } - if (!string.IsNullOrEmpty(link.Title)) + // Write any additional attributes (like width/height from styling instructions) + _ = renderer.WriteAttributes(link); + + // Always use alt text as title for accessibility consistency + if (link.FirstChild != null) { _ = renderer.Write(" title=\""); - _ = renderer.WriteEscape(link.Title); + renderer.WriteChildren(link); _ = renderer.Write('"'); } - // Write any additional attributes (like width/height from styling instructions) - _ = renderer.WriteAttributes(link); - _ = renderer.Write(" />"); } diff --git a/tests/Elastic.Markdown.Tests/Inline/InlineImageTest.cs b/tests/Elastic.Markdown.Tests/Inline/InlineImageTest.cs index 0d372b0575..ffe4cc40c6 100644 --- a/tests/Elastic.Markdown.Tests/Inline/InlineImageTest.cs +++ b/tests/Elastic.Markdown.Tests/Inline/InlineImageTest.cs @@ -19,7 +19,7 @@ public class InlineImageTest(ITestOutputHelper output) : InlineTest( public void GeneratesAttributesInHtml() => // language=html Html.ShouldContainHtml( - """

Elasticsearch

""" + """

Elasticsearch

""" ); } @@ -36,7 +36,7 @@ public class RelativeInlineImageTest(ITestOutputHelper output) : InlineTest // language=html Html.ShouldContainHtml( - """

Elasticsearch

""" + """

Elasticsearch

""" ); } @@ -54,7 +54,7 @@ public class InlineImageWithSizingSpaceBeforeTest(ITestOutputHelper output) : In public void GeneratesAttributesInHtml() => // language=html Html.ShouldContainHtml( - """

Elasticsearch

""" + """

Elasticsearch

""" ); } @@ -72,7 +72,7 @@ public class InlineImageWithSizingNoSpaceBeforeTest(ITestOutputHelper output) : public void GeneratesAttributesInHtml() => // language=html Html.ShouldContainHtml( - """

Elasticsearch

""" + """

Elasticsearch

""" ); } @@ -90,11 +90,11 @@ public class InlineImageWithPixelSizingTest(ITestOutputHelper output) : InlineTe public void GeneratesAttributesInHtml() => // language=html Html.ShouldContainHtml( - """

Elasticsearch

""" + """

Elasticsearch

""" ); } -// Test image sizing with title and sizing +// Test image sizing with title and sizing — explicit title in markdown is ignored; alt text is always used as title public class InlineImageWithTitleAndSizingTest(ITestOutputHelper output) : InlineTest(output, """ ![Elasticsearch](/_static/img/observability.png "My Title =50%") @@ -108,7 +108,7 @@ public class InlineImageWithTitleAndSizingTest(ITestOutputHelper output) : Inlin public void GeneratesAttributesInHtml() => // language=html Html.ShouldContainHtml( - """

Elasticsearch

""" + """

Elasticsearch

""" ); } @@ -126,6 +126,6 @@ public class InlineImageWithWidthOnlyTest(ITestOutputHelper output) : InlineTest public void GeneratesAttributesInHtml() => // language=html Html.ShouldContainHtml( - """

Elasticsearch

""" + """

Elasticsearch

""" ); } diff --git a/tests/Elastic.Markdown.Tests/Inline/SubstitutionTest.cs b/tests/Elastic.Markdown.Tests/Inline/SubstitutionTest.cs index 177e303e74..60c53be3d0 100644 --- a/tests/Elastic.Markdown.Tests/Inline/SubstitutionTest.cs +++ b/tests/Elastic.Markdown.Tests/Inline/SubstitutionTest.cs @@ -203,7 +203,7 @@ public class ReplaceInImageTitle(ITestOutputHelper output) : InlineTest(output, [Fact] public void OnlySeesGlobalVariable() => Html.Should().NotContain("title=\"{{hello-world}}\"") - .And.Contain("title=\"Hello World\""); + .And.Contain("title=\"Observability\""); } public class MutationOperatorTest(ITestOutputHelper output) : InlineTest(output,