Skip to content

Commit 10fcfc9

Browse files
committed
Implemented better regex attribute comparison syntax
1 parent 581fc60 commit 10fcfc9

21 files changed

+188
-100
lines changed

src/Diffing/CssClassAttributeDifferenceEvaluator.cs

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Text.RegularExpressions;
6+
using System.Threading.Tasks;
7+
using System.Xml;
8+
using Egil.RazorComponents.Testing.Rendering;
9+
using Org.XmlUnit.Diff;
10+
11+
namespace Egil.RazorComponents.Testing.Diffing
12+
{
13+
public static class HtmlDifferenceEvaluators
14+
{
15+
private const string RegexNamespace = RenderedFactXmlDocumentExtensions.RegexXmlNamespace;
16+
private readonly static char[] Space = new char[] { ' ' };
17+
18+
public static ComparisonResult RegexAttributeDifferenceEvaluator(Comparison comparison, ComparisonResult outcome)
19+
{
20+
if (comparison is null) throw new ArgumentNullException(nameof(comparison));
21+
22+
if (outcome == ComparisonResult.EQUAL) return outcome;
23+
if (comparison.Type != ComparisonType.ATTR_NAME_LOOKUP) return outcome;
24+
25+
if (comparison.ControlDetails.Value is XmlQualifiedName a1 && a1.Namespace == RegexNamespace)
26+
{
27+
var testAttr = comparison.TestDetails.Target.Attributes.GetNamedItem(a1.Name);
28+
var ctrlAttr = comparison.ControlDetails.Target.Attributes.GetNamedItem(a1.Name, a1.Namespace);
29+
if (Regex.IsMatch(testAttr.Value, ctrlAttr.Value))
30+
{
31+
return ComparisonResult.EQUAL;
32+
}
33+
}
34+
else if (comparison.TestDetails.Value is XmlQualifiedName a2 && comparison.ControlDetails.Target.Attributes.GetNamedItem(a2.Name, RegexNamespace) is XmlNode ctrlAttr)
35+
{
36+
return ComparisonResult.EQUAL;
37+
}
38+
return outcome;
39+
}
40+
41+
public static ComparisonResult CssClassAttributeDifferenceEvaluator(Comparison comparison, ComparisonResult outcome)
42+
{
43+
if (comparison is null) throw new ArgumentNullException(nameof(comparison));
44+
45+
if (outcome == ComparisonResult.EQUAL) return outcome;
46+
if (comparison.Type != ComparisonType.ATTR_VALUE) return outcome;
47+
if (!comparison.TestDetails.Target.Name.Equals("class", StringComparison.OrdinalIgnoreCase)) return outcome;
48+
49+
// BANG: Value should not be null on ControlDetails and TestDetails.
50+
var expected = comparison.ControlDetails.Value.ToString()!.Split(Space, StringSplitOptions.RemoveEmptyEntries);
51+
var actual = comparison.TestDetails.Value.ToString()!.Split(Space, StringSplitOptions.RemoveEmptyEntries);
52+
53+
if (SetEqual(expected, actual)) return ComparisonResult.EQUAL;
54+
else return outcome;
55+
}
56+
57+
private static bool SetEqual(string[] expected, string[] actual)
58+
{
59+
if (expected.Length != actual.Length) return false;
60+
return new HashSet<string>(expected).SetEquals(new HashSet<string>(actual));
61+
}
62+
}
63+
}

src/Diffing/RazorComponentDoesNotMatchException.cs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
1-
using System;
2-
using System.Linq;
1+
using System.Linq;
32
using System.Text;
43
using System.Xml;
54
using Org.XmlUnit.Diff;
65
using Xunit.Sdk;
76

8-
namespace Egil.RazorComponents.Testing
7+
namespace Egil.RazorComponents.Testing.Diffing
98
{
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-
9+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "<Pending>")]
1910
public class RazorComponentDoesNotMatchException : AssertActualExpectedException
2011
{
2112
public RazorComponentDoesNotMatchException(XmlNode expectedHtml, XmlNode renderedHtml, Diff diffResult)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Xml;
3+
using Egil.RazorComponents.Testing.Diffing;
4+
using Xunit.Sdk;
5+
6+
namespace Egil.RazorComponents.Testing
7+
{
8+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "<Pending>")]
9+
public class RazorComponentsMatchException : XunitException
10+
{
11+
public RazorComponentsMatchException(XmlNode expectedHtml) : base($"Expected HTML and Rendered HTML should not match." +
12+
$"The expected HTML was:{Environment.NewLine}" +
13+
$"{expectedHtml.PrettyXml()}")
14+
{
15+
}
16+
}
17+
}

src/Diffing/RegexAttributeDifferenceEvaluator.cs

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

src/Diffing/XmlNodeAssertExtensions.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Text;
34
using System.Xml;
5+
using Egil.RazorComponents.Testing.Diffing;
46
using Org.XmlUnit;
57
using Org.XmlUnit.Builder;
68
using Org.XmlUnit.Diff;
@@ -9,16 +11,22 @@ namespace Egil.RazorComponents.Testing
911
{
1012

1113
public static class XmlNodeAssertExtensions
12-
{
14+
{
1315
public static void ShouldBe(this XmlNode renderedHtml, XmlNode expectedHtml)
1416
{
17+
if (renderedHtml is null) throw new ArgumentNullException(nameof(renderedHtml));
18+
if (expectedHtml is null) throw new ArgumentNullException(nameof(expectedHtml));
19+
1520
var diffResult = CreateDiff(expectedHtml, renderedHtml);
1621
if (diffResult.HasDifferences())
1722
throw new RazorComponentDoesNotMatchException(expectedHtml.FirstChild, renderedHtml.FirstChild, diffResult);
1823
}
1924

2025
public static void ShouldNotBe(this XmlNode renderedHtml, XmlNode expectedHtml)
2126
{
27+
if (renderedHtml is null) throw new ArgumentNullException(nameof(renderedHtml));
28+
if (expectedHtml is null) throw new ArgumentNullException(nameof(expectedHtml));
29+
2230
var diffResult = CreateDiff(expectedHtml, renderedHtml);
2331
if (!diffResult.HasDifferences())
2432
throw new RazorComponentsMatchException(expectedHtml.FirstChild);
@@ -35,11 +43,12 @@ public static Diff CreateDiff(ISource control, ISource test)
3543
{
3644
return DiffBuilder.Compare(control)
3745
.IgnoreWhitespace()
46+
.WithNamespaceContext(new Dictionary<string, string>() { { "regex", "urn:egil.razorcomponents.testing.library.regex" } })
3847
.WithTest(test)
3948
.WithDifferenceEvaluator(DifferenceEvaluators.Chain(
4049
DifferenceEvaluators.Default,
41-
RegexAttributeDifferenceEvaluator.Default,
42-
CssClassAttributeDifferenceEvaluator.Default)
50+
HtmlDifferenceEvaluators.RegexAttributeDifferenceEvaluator,
51+
HtmlDifferenceEvaluators.CssClassAttributeDifferenceEvaluator)
4352
)
4453
.Build();
4554
}

src/Egil.RazorComponents.Testing.Library.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
<ItemGroup>
2626
<PackageReference Include="Microsoft.AspNetCore.Components" Version="$(AspNetCoreVersion)" />
2727
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="$(AspNetCoreVersion)" />
28-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
28+
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4">
29+
<PrivateAssets>all</PrivateAssets>
30+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
31+
</PackageReference>
2932
<PackageReference Include="XMLUnit.Core" Version="2.7.1" />
3033
<PackageReference Include="xunit.assert" Version="2.4.1" />
3134
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />

src/Fact.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Diagnostics.CodeAnalysis;
23
using Microsoft.AspNetCore.Components;
34
using Microsoft.AspNetCore.Components.Rendering;
45
using Microsoft.AspNetCore.Components.RenderTree;
@@ -18,9 +19,11 @@ public class Fact : ComponentBase
1819
[Parameter]
1920
public RenderFragment? ChildContent { get; set; }
2021

22+
[SuppressMessage("Usage", "CA2208:Instantiate argument exceptions correctly", Justification = "<Pending>")]
2123
protected override void BuildRenderTree(RenderTreeBuilder builder)
2224
{
2325
if (ChildContent is null) throw new ArgumentNullException(nameof(ChildContent));
26+
if (builder is null) throw new ArgumentNullException(nameof(builder));
2427

2528
builder.OpenElement(0, ElementName);
2629

@@ -50,6 +53,8 @@ public abstract class FactPart : ComponentBase
5053

5154
protected override void BuildRenderTree(RenderTreeBuilder builder)
5255
{
56+
if (builder is null) throw new ArgumentNullException(nameof(builder));
57+
5358
builder.OpenElement(0, GetElementName());
5459

5560
if (!string.IsNullOrEmpty(Id))

src/GlobalSuppressions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@
22
Justification = "I will take the chance",
33
Scope = "namespaceanddescendants",
44
Target = "Egil.RazorComponents.Testing")]
5+
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization",
6+
"CA1303:Do not pass literals as localized parameters",
7+
Justification = "<Pending>",
8+
Scope = "namespaceanddescendants",
9+
Target = "Egil.RazorComponents.Testing")]

src/RazorComponentRenderResultParseException.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Egil.RazorComponents.Testing
66
{
7+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "<Pending>")]
78
public class RazorComponentRenderResultParseException : XunitException
89
{
910
public RazorComponentRenderResultParseException(string userMessage, Exception innerException) : base(userMessage, innerException)

0 commit comments

Comments
 (0)