Skip to content

Commit 581fc60

Browse files
committed
Workaround for #13 where self-closing were non-closed by razor compiler even if closed by user.
Additional unit tests of library added.
1 parent 7f4b21d commit 581fc60

8 files changed

+190
-75
lines changed

src/Diffing/RazorComponentDoesNotMatchException.cs

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,20 @@
77

88
namespace Egil.RazorComponents.Testing
99
{
10+
public class RazorComponentsMatchException : XunitException
11+
{
12+
public RazorComponentsMatchException(XmlNode expectedHtml) : base($"Expected HTML and Rendered HTML should not match." +
13+
$"The expected HTML was:{Environment.NewLine}" +
14+
$"{expectedHtml.PrettyXml()}")
15+
{
16+
}
17+
}
18+
1019
public class RazorComponentDoesNotMatchException : AssertActualExpectedException
1120
{
1221
public RazorComponentDoesNotMatchException(XmlNode expectedHtml, XmlNode renderedHtml, Diff diffResult)
13-
: base(PrettyXml(expectedHtml),
14-
PrettyXml(renderedHtml),
22+
: base(expectedHtml.PrettyXml(),
23+
renderedHtml.PrettyXml(),
1524
CreateDiffMessage(diffResult),
1625
"Expected HTML",
1726
"Rendered HTML")
@@ -32,30 +41,5 @@ private static string CreateDiffMessage(Diff diffResult)
3241

3342
return result.ToString();
3443
}
35-
36-
private static string PrettyXml(XmlNode? xml)
37-
{
38-
if (xml is null) return string.Empty;
39-
40-
var result = new StringBuilder();
41-
var settings = new XmlWriterSettings
42-
{
43-
OmitXmlDeclaration = true,
44-
Indent = true,
45-
NewLineOnAttributes = false,
46-
ConformanceLevel = ConformanceLevel.Fragment,
47-
IndentChars = " ",
48-
};
49-
50-
using (var xmlWriter = XmlWriter.Create(result, settings))
51-
{
52-
xml.WriteTo(xmlWriter);
53-
}
54-
55-
result.Insert(0, Environment.NewLine);
56-
result.AppendLine();
57-
58-
return result.ToString();
59-
}
6044
}
6145
}

src/Diffing/XmlNodeAssertExtensions.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Xml;
1+
using System;
2+
using System.Text;
3+
using System.Xml;
24
using Org.XmlUnit;
35
using Org.XmlUnit.Builder;
46
using Org.XmlUnit.Diff;
@@ -15,6 +17,13 @@ public static void ShouldBe(this XmlNode renderedHtml, XmlNode expectedHtml)
1517
throw new RazorComponentDoesNotMatchException(expectedHtml.FirstChild, renderedHtml.FirstChild, diffResult);
1618
}
1719

20+
public static void ShouldNotBe(this XmlNode renderedHtml, XmlNode expectedHtml)
21+
{
22+
var diffResult = CreateDiff(expectedHtml, renderedHtml);
23+
if (!diffResult.HasDifferences())
24+
throw new RazorComponentsMatchException(expectedHtml.FirstChild);
25+
}
26+
1827
private static Diff CreateDiff(XmlNode control, XmlNode test)
1928
{
2029
var controlSource = Input.FromNode(control.FirstChild).Build();
@@ -34,5 +43,30 @@ public static Diff CreateDiff(ISource control, ISource test)
3443
)
3544
.Build();
3645
}
46+
47+
public static string PrettyXml(this XmlNode? xml)
48+
{
49+
if (xml is null) return string.Empty;
50+
51+
var result = new StringBuilder();
52+
var settings = new XmlWriterSettings
53+
{
54+
OmitXmlDeclaration = true,
55+
Indent = true,
56+
NewLineOnAttributes = false,
57+
ConformanceLevel = ConformanceLevel.Fragment,
58+
IndentChars = " ",
59+
};
60+
61+
using (var xmlWriter = XmlWriter.Create(result, settings))
62+
{
63+
xml.WriteTo(xmlWriter);
64+
}
65+
66+
result.Insert(0, Environment.NewLine);
67+
result.AppendLine();
68+
69+
return result.ToString();
70+
}
3771
}
3872
}

src/Egil.RazorComponents.Testing.Library.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<RepositoryUrl>https://github.com/egil/razor-components-testing-library</RepositoryUrl>
1414
<PackageProjectUrl>https://github.com/egil/razor-components-testing-library</PackageProjectUrl>
1515
<PackageTags>razor-components unit-testing testing blazor blazor-server-side blazor-client-side</PackageTags>
16-
<Version>0.1.0-preview9-19424-4-1</Version>
16+
<Version>0.1.0-preview9-19424-4-2</Version>
1717
<Authors>Egil Hansen</Authors>
1818
<Company>Egil Hansen</Company>
1919
<Product>Razor Component Testing Library</Product>

src/Rendering/RazerComponentTestRenderer.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Runtime.ExceptionServices;
44
using System.Text.Encodings.Web;
5+
using System.Text.RegularExpressions;
56
using System.Threading.Tasks;
67
using System.Xml;
78
using Microsoft.AspNetCore.Components;
@@ -79,6 +80,8 @@ private static IReadOnlyList<TestRenderResult> ProcessRenderResult(string render
7980

8081
private static XmlDocument LoadRenderResult(string renderResults)
8182
{
83+
// Workaround for https://github.com/egil/razor-components-testing-library/issues/13 and https://github.com/aspnet/AspNetCore/issues/13793
84+
renderResults = EscapeSelfClosingTags(renderResults);
8285
var renderResultXml = $"<{RenderResultsElement}>{renderResults}</{RenderResultsElement}>";
8386
var xml = new XmlDocument();
8487
try
@@ -93,5 +96,12 @@ private static XmlDocument LoadRenderResult(string renderResults)
9396
$"{renderResultXml}", ex);
9497
}
9598
}
99+
100+
private static readonly Regex SelfClosingTagsFinder = new Regex(@"<(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
101+
102+
private static string EscapeSelfClosingTags(string html)
103+
{
104+
return SelfClosingTagsFinder.Replace(html, "<$1/>");
105+
}
96106
}
97107
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Xml;
7+
using Shouldly;
8+
using Xunit;
9+
10+
namespace Egil.RazorComponents.Testing.Library.Diffing
11+
{
12+
public class XmlNodeAssertExtensionsTests
13+
{
14+
[Fact(DisplayName = "xmlNode.ShouldBe(xmlNode) throws correct exception when nodes are different")]
15+
public void MyTestMethod()
16+
{
17+
var (renderedHtml, expectedHtml) = CreateTestXml(
18+
$"<h1 class='foo'></h1>",
19+
$"<div class='bar'></div>"
20+
);
21+
22+
Should.Throw<RazorComponentDoesNotMatchException>(() => renderedHtml.ShouldBe(expectedHtml));
23+
}
24+
25+
[Fact(DisplayName = "ShouldNotMatch does not throws exception when expected and rendered does not matches")]
26+
public void ShoudNotMatchDoesNotThrowsWhenNoMatche()
27+
{
28+
var (renderedHtml, expectedHtml) = CreateTestXml(
29+
$"<h1 class='foo'></h1>",
30+
$"<div class='bar'></div>"
31+
);
32+
33+
renderedHtml.ShouldNotBe(expectedHtml);
34+
}
35+
36+
[Fact(DisplayName = "ShouldNotMatch throws exception when expected and rendered matches")]
37+
public void ShoudNotMatchThrowsWhenMatches()
38+
{
39+
var (renderedHtml, expectedHtml) = CreateTestXml(
40+
$"<div class='foo'></div>",
41+
$"<div class='foo'></div>"
42+
);
43+
44+
Should.Throw<RazorComponentsMatchException>(() => renderedHtml.ShouldNotBe(expectedHtml));
45+
}
46+
47+
[Theory(DisplayName = "Order of classes and whitespace in class='...' attribute doesn't matter when comparing")]
48+
[InlineData("", " ")]
49+
[InlineData("foo bar", "foo bar")]
50+
[InlineData("foo bar", "bar foo")]
51+
[InlineData("foo bar", "foo bar")]
52+
[InlineData("foo bar", "foo bar")]
53+
[InlineData(" foo bar", "foo bar ")]
54+
[InlineData(" foo bar", " bar foo")]
55+
public void ClassAttributeOrderTest(string inputClass, string expectedOutput)
56+
{
57+
var (renderedHtml, expectedHtml) = CreateTestXml(
58+
$"<div class='{inputClass}'></div>",
59+
$"<div class='{expectedOutput}'></div>"
60+
);
61+
62+
renderedHtml.ShouldBe(expectedHtml);
63+
}
64+
65+
[Fact(DisplayName = "Expected HTML with 'RegEx:' at the start of their attribute uses the following regex string to compare attribute")]
66+
public void RegexAttrTest()
67+
{
68+
var (renderedHtml, expectedHtml) = CreateTestXml(
69+
$"<div class='id-{GetHashCode()}'></div>",
70+
$"<div class='RegEx:^id-[\\d]+$'></div>"
71+
);
72+
73+
renderedHtml.ShouldBe(expectedHtml);
74+
}
75+
76+
private static (XmlNode RenderedHtml, XmlNode ExpectedHtml) CreateTestXml(string renderedHtml, string expectedHtml)
77+
{
78+
var doc = new XmlDocument();
79+
doc.LoadXml("<RenderResult>" +
80+
$"<RenderedHtml><Html>{renderedHtml}</Html></RenderedHtml>" +
81+
$"<ExpectedHtml><Html>{expectedHtml}</Html></ExpectedHtml>" +
82+
"</RenderResult>");
83+
84+
var test = doc.SelectSingleNode("RenderResult/RenderedHtml");
85+
var control = doc.SelectSingleNode("RenderResult/ExpectedHtml");
86+
87+
return (test, control);
88+
}
89+
90+
91+
}
92+
}

tests/RazorComponentTestRendererTest.cs

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
@inherits Egil.RazorComponents.Testing.RazorComponentTest
2+
@using Egil.RazorComponents.Testing
3+
4+
<Fact DisplayName="Self-closing tags are compared correctly">
5+
<TestSetup>
6+
<div>
7+
<area />
8+
<base />
9+
<br />
10+
<col />
11+
<embed />
12+
<hr />
13+
<img />
14+
<input />
15+
<link />
16+
<meta />
17+
<param />
18+
<source />
19+
<track />
20+
<wbr />
21+
</div>
22+
</TestSetup>
23+
<ExpectedHtml>
24+
<div>
25+
<area />
26+
<base />
27+
<br />
28+
<col />
29+
<embed />
30+
<hr />
31+
<img />
32+
<input />
33+
<link />
34+
<meta />
35+
<param />
36+
<source />
37+
<track />
38+
<wbr />
39+
</div>
40+
</ExpectedHtml>
41+
</Fact>

tests/XmlNodeAssertExtensionsTests.cs

Lines changed: 0 additions & 29 deletions
This file was deleted.

0 commit comments

Comments
 (0)