Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion src/AngleSharp.Diffing.Tests/DiffBuilderTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
using AngleSharp.Diffing.Strategies;
using AngleSharp.Diffing.Strategies.AttributeStrategies;
using AngleSharp.Diffing.Strategies.TextNodeStrategies;
using AngleSharp.Diffing.TestData;

namespace AngleSharp.Diffing;


Expand Down Expand Up @@ -116,4 +121,38 @@ public void Test006(string control, string test)

diffs.ShouldBeEmpty();
}
}

[Theory(DisplayName =
"When a control element has ':ignore', elements with and without that attribute should return empty diffs")]
[MemberData(nameof(IgnoreAttributeTestData.ControlAndHtmlData), MemberType = typeof(IgnoreAttributeTestData))]
public void Test007(string controlHtml, string testHtml)
{
var diffs = DiffBuilder.Compare(controlHtml).WithTest(testHtml).Build();
Assert.Empty(diffs);
}

[Theory(DisplayName =
"When a control element has ':ignore', but IgnoreAttributeComparer is not active, diffs should be found")]
[MemberData(nameof(IgnoreAttributeTestData.ControlHtmlAndDiffData), MemberType = typeof(IgnoreAttributeTestData))]
public void Test008(string controlHtml, string testHtml, DiffResult expectedDiffResult)
{
var diffs = DiffBuilder
.Compare(controlHtml)
.WithTest(testHtml)
.WithOptions(a => a // Most important thing to note here is we do not have a ignore attribute comparer
.AddSearchingNodeMatcher()
.AddMatcher(AttributeNameMatcher.Match, StrategyType.Generalized)
.AddElementComparer(enforceTagClosing: false)
.AddMatcher(PostfixedAttributeMatcher.Match, StrategyType.Specialized)
.AddComparer(AttributeComparer.Compare, StrategyType.Generalized)
.AddClassAttributeComparer()
.AddBooleanAttributeComparer(BooleanAttributeComparision.Strict)
.AddStyleAttributeComparer())
.Build()
.ToList();

Assert.Single(diffs);
Assert.Equal(DiffTarget.Attribute, diffs[0].Target);
Assert.Equal(expectedDiffResult, diffs[0].Result);
}
}
45 changes: 45 additions & 0 deletions src/AngleSharp.Diffing.Tests/TestData/IgnoreAttributeTestData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
namespace AngleSharp.Diffing.TestData;

internal static class IgnoreAttributeTestData
{
public static TheoryData<string, string> ControlAndHtmlData()
{
var theoryData = new TheoryData<string, string>();
foreach (var (controlHtml, expectedHtml, _) in TestCases)
{
theoryData.Add(controlHtml, expectedHtml);
}

return theoryData;
}

public static TheoryData<string, string, DiffResult> ControlHtmlAndDiffData()
{
var theoryData = new TheoryData<string, string, DiffResult>();
foreach (var (controlHtml, expectedHtml, expectedDiffResult) in TestCases)
{
theoryData.Add(controlHtml, expectedHtml, expectedDiffResult);
}

return theoryData;
}

private static readonly IEnumerable<(string controlHtml, string expectedHtml, DiffResult expectedDiffResult)>
TestCases =
[
("<div class:ignore></div>", "<div class=\"ian-fleming\"></div>", DiffResult.Different),
("<div class:ignore></div>", "<div class=\"\"></div>", DiffResult.Different),
("<div class:ignore></div>", "<div class></div>", DiffResult.Different),
("<div class:ignore></div>", "<div></div>", DiffResult.Missing),
("<input required:ignore/>", "<input required=\"required\"/>", DiffResult.Different),
("<input required:ignore/>", "<input required=\"\"/>", DiffResult.Different),
("<input required:ignore/>", "<input required/>", DiffResult.Different),
("<input required:ignore/>", "<input/>", DiffResult.Missing),
("<button onclick:ignore/></button>", "<button onclick=\"alert(1)\"></button>", DiffResult.Different),
("<button onclick:ignore/></button>", "<button/></button>", DiffResult.Missing),
("<a aria-disabled:ignore/></a>", "<a aria-disabled=\"true\"/></a>", DiffResult.Different),
("<a aria-disabled:ignore/></a>", "<a/></a>", DiffResult.Missing),
("<span style:ignore/></span>", "<span style=\"color:red;\"/></span>", DiffResult.Different),
("<span style:ignore/></span>", "<span/></span>", DiffResult.Missing),
];
}
1 change: 1 addition & 0 deletions src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AngleSharp.Diffing.Core.Diffs;
using AngleSharp.Diffing.Strategies.AttributeStrategies;

namespace AngleSharp.Diffing.Core;

Expand Down
2 changes: 1 addition & 1 deletion src/AngleSharp.Diffing/Core/SourceMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public IEnumerable<AttributeComparisonSource> GetUnmatched()
{
foreach (var source in _sources.Values)
{
if (!_matched.Contains(source.Attribute.Name))
if (IsUnmatched(source.Attribute.Name))
yield return source;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static IDiffingStrategyCollection IgnoreDiffAttributes(this IDiffingStrat
public static IDiffingStrategyCollection AddAttributeNameMatcher(this IDiffingStrategyCollection builder)
{
builder.AddMatcher(AttributeNameMatcher.Match, StrategyType.Generalized);
builder.AddMatcher(IgnoreAttributeMatcher.Match, StrategyType.Generalized);
return builder;
}

Expand Down
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe revert this change?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe merge the two files into one static file, e.g. IgnoreAttributeStrategy.cs, have the Compare and Match methods in the static class, and then have the static IsIgnoreAttribute method there being shared.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to keep IgnoreAttributeComparer for backwards compatibility?

Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ public static CompareResult Compare(in AttributeComparison comparison, CompareRe
if (currentDecision.IsSameOrSkip)
return currentDecision;

return comparison.Control.Attribute.Name.EndsWith(DIFF_IGNORE_POSTFIX, StringComparison.OrdinalIgnoreCase)
return IsIgnoreAttribute(comparison.Control.Attribute)
? CompareResult.Same
: currentDecision;
}

public static bool IsIgnoreAttribute(IAttr source)
{
return source.Name.EndsWith(DIFF_IGNORE_POSTFIX, StringComparison.OrdinalIgnoreCase);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace AngleSharp.Diffing.Strategies.AttributeStrategies;

/// <summary>
/// Ignore Attribute matcher strategy.
/// </summary>
public static class IgnoreAttributeMatcher
{
private const string DIFF_IGNORE_POSTFIX = ":ignore";

/// <summary>
/// Attribute name matcher strategy.
/// </summary>
public static IEnumerable<AttributeComparison> Match(IDiffContext context, SourceMap controlSources, SourceMap testSources)
{
if (controlSources is null)
throw new ArgumentNullException(nameof(controlSources));
if (testSources is null)
throw new ArgumentNullException(nameof(testSources));

foreach (var control in controlSources.GetUnmatched())
{
// An unmatched :ignore attribute can just be matched with itself if it isn't
// matched with a "test" attribute of the same name already.
// this means an ignored attribute is ignored even if it does not appear in the test html.
if (control.Attribute.Name.EndsWith(DIFF_IGNORE_POSTFIX, StringComparison.OrdinalIgnoreCase))
yield return new AttributeComparison(control, control);
}
}
}
Loading