Skip to content

Commit 38875ff

Browse files
committed
fix: element comparer includes toggle to enforce check of closing tag
1 parent 6fb2ebe commit 38875ff

File tree

8 files changed

+415
-455
lines changed

8 files changed

+415
-455
lines changed

Diff for: CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 0.18.1
2+
3+
- Fixed element comparer such that it can strictly check if the closing tags in the source markup is the same.
4+
15
# 0.18.0
26

37
- Added a new comparer, which ensures element tags are closed the same way, e.g. `<br> and <br />` would not be considered equal, but `<br>` and `<br>` would be.

Diff for: src/AngleSharp.Diffing.Tests/Strategies/DiffingStrategyPipelineTest.cs

+301-308
Large diffs are not rendered by default.

Diff for: src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/ElementClosingComparerTest.cs

-41
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
namespace AngleSharp.Diffing.Strategies.ElementStrategies;
2+
3+
public class ElementComparerTest : DiffingTestBase
4+
{
5+
public ElementComparerTest(DiffingTestFixture fixture) : base(fixture)
6+
{
7+
}
8+
9+
[Theory(DisplayName = "When control and test nodes have the same type and name and enforceTagClosing is false, the result is Same")]
10+
[InlineData("<p>", "<p />")]
11+
[InlineData("<br>", "<br/>")]
12+
[InlineData("<br>", "<br>")]
13+
[InlineData("<p>", "<p>")]
14+
public void Test001(string controlHtml, string testHtml)
15+
{
16+
var comparison = ToComparison(controlHtml, testHtml);
17+
new ElementComparer(enforceTagClosing: false)
18+
.Compare(comparison, CompareResult.Unknown)
19+
.ShouldBe(CompareResult.Same);
20+
}
21+
22+
[Theory(DisplayName = "When control and test nodes have the a different type and name, the result is Different")]
23+
[InlineData("<div>", "<p>", false)]
24+
[InlineData("<div>", "textnode", false)]
25+
[InlineData("<div>", "<!--comment-->", false)]
26+
[InlineData("<!--comment-->", "textnode", false)]
27+
[InlineData("<br>", "<br/>", true)]
28+
[InlineData("<input>", "<input/>", true)]
29+
[InlineData("<div>", "<p>", true)]
30+
[InlineData("<div>", "textnode", true)]
31+
[InlineData("<div>", "<!--comment-->", true)]
32+
[InlineData("<!--comment-->", "textnode", true)]
33+
public void Test002(string controlHtml, string testHtml, bool enforceTagClosing)
34+
{
35+
var comparison = ToComparison(controlHtml, testHtml);
36+
new ElementComparer(enforceTagClosing)
37+
.Compare(comparison, CompareResult.Unknown)
38+
.ShouldBe(CompareResult.Different);
39+
}
40+
41+
[Theory(DisplayName = "When unknown node is used in comparison, but node name is equal, the result is Same")]
42+
[InlineData("<svg><path></path></svg>", "<path/>")]
43+
public void HandleUnknownNodeDuringComparison(string controlHtml, string testHtml)
44+
{
45+
var knownNode = ToNode(controlHtml).FirstChild.ToComparisonSource(0, ComparisonSourceType.Control);
46+
var unknownNode = ToNode(testHtml).ToComparisonSource(0, ComparisonSourceType.Test);
47+
var comparison = new Comparison(knownNode, unknownNode);
48+
49+
new ElementComparer(enforceTagClosing: false)
50+
.Compare(comparison, CompareResult.Unknown)
51+
.ShouldBe(CompareResult.Same);
52+
}
53+
}

Diff for: src/AngleSharp.Diffing.Tests/Strategies/NodeStrategies/NodeComparerTest.cs

-47
This file was deleted.

Diff for: src/AngleSharp.Diffing/Strategies/ElementStrategies/DiffingStrategyPipelineBuilderExtensions.cs

+12-17
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,19 @@ namespace AngleSharp.Diffing;
55

66
public static partial class DiffingStrategyPipelineBuilderExtensions
77
{
8+
/// <summary>
9+
/// Enables the basic element comparer, that checks if two nodes are element nodes and have the same name.
10+
/// </summary>
11+
/// <param name="builder">The collection to add the comparer to.</param>
12+
/// <param name="enforceTagClosing">
13+
/// Whether or not the closing style of
14+
/// an element is considered during comparison.
15+
/// </param>
16+
public static IDiffingStrategyCollection AddElementComparer(this IDiffingStrategyCollection builder, bool enforceTagClosing)
817
{
9-
/// <summary>
10-
/// Enables the basic element comparer, that checks if two nodes are element nodes and have the same name.
11-
/// </summary>
12-
public static IDiffingStrategyCollection AddElementComparer(this IDiffingStrategyCollection builder)
13-
{
14-
builder.AddComparer(ElementComparer.Compare, StrategyType.Generalized);
15-
return builder;
16-
}
17-
18-
/// <summary>
19-
/// Adds an element comparer that ensures element tags are closed the same way, e.g. `&lt;br&gt;` and `&lt;br /&gt;` would not be considered equal, but `&lt;br&gt;` and `&lt;br&gt;` would be.
20-
/// </summary>
21-
public static IDiffingStrategyCollection AddElementClosingComparer(this IDiffingStrategyCollection builder)
22-
{
23-
builder.AddComparer(ElementClosingComparer.Compare, StrategyType.Generalized);
24-
return builder;
25-
}
18+
builder.AddComparer(new ElementComparer(enforceTagClosing).Compare, StrategyType.Generalized);
19+
return builder;
20+
}
2621

2722
/// <summary>
2823
/// Enables the CSS-selector matcher strategy during diffing.

Diff for: src/AngleSharp.Diffing/Strategies/ElementStrategies/ElementClosingComparer.cs

-29
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,55 @@
1-
using AngleSharp.Diffing.Core;
2-
using AngleSharp.Dom;
1+
using AngleSharp.Html.Parser.Tokens;
32

4-
namespace AngleSharp.Diffing.Strategies.ElementStrategies
3+
namespace AngleSharp.Diffing.Strategies.ElementStrategies;
4+
5+
/// <summary>
6+
/// Represents the element comparer strategy.
7+
/// </summary>
8+
public class ElementComparer
59
{
610
/// <summary>
7-
/// Represents the element comparer strategy.
11+
/// Gets whether or not the closing style of
12+
/// an element is considered during comparison.
13+
/// </summary>
14+
public bool EnforceTagClosing { get; }
15+
16+
/// <summary>
17+
/// Creates an instance of the <see cref="ElementComparer"/>.
18+
/// </summary>
19+
/// <param name="enforceTagClosing">
20+
/// Whether or not the closing style of
21+
/// an element is considered during comparison.
22+
/// </param>
23+
public ElementComparer(bool enforceTagClosing)
24+
{
25+
EnforceTagClosing = enforceTagClosing;
26+
}
27+
28+
/// <summary>
29+
/// The element comparer strategy.
830
/// </summary>
9-
public static class ElementComparer
31+
public CompareResult Compare(in Comparison comparison, CompareResult currentDecision)
1032
{
11-
/// <summary>
12-
/// The element comparer strategy.
13-
/// </summary>
14-
public static CompareResult Compare(in Comparison comparison, CompareResult currentDecision)
33+
if (currentDecision.IsSameOrSkip())
34+
return currentDecision;
35+
36+
var result = comparison.Control.Node.NodeType == NodeType.Element && comparison.AreNodeTypesEqual
37+
? CompareResult.Same
38+
: CompareResult.Different;
39+
40+
if (EnforceTagClosing && result == CompareResult.Same)
1541
{
16-
if (currentDecision.IsSameOrSkip())
17-
return currentDecision;
18-
return comparison.Control.Node.NodeType == NodeType.Element && comparison.AreNodeTypesEqual
19-
? CompareResult.Same
42+
if (comparison.Test.Node is not IElement testElement || testElement.SourceReference is not HtmlTagToken testTag)
43+
throw new InvalidOperationException("No source reference attached to test element, cannot determine element tag closing style.");
44+
45+
if (comparison.Control.Node is not IElement controlElement || controlElement.SourceReference is not HtmlTagToken controlTag)
46+
throw new InvalidOperationException("No source reference attached to test element, cannot determine element tag closing style.");
47+
48+
return testTag.IsSelfClosing == controlTag.IsSelfClosing
49+
? result
2050
: CompareResult.Different;
2151
}
52+
53+
return result;
2254
}
2355
}

0 commit comments

Comments
 (0)