Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void Test000(CompareResult currentResult)
@"<p foo=""bar""></p>", "foo"
);

IgnoreAttributeComparer
IgnoreAttributeStrategy
.Compare(comparison, currentResult)
.ShouldBe(currentResult);
}
Expand All @@ -30,8 +30,8 @@ public void Test003()
@"<p foo=""bar""></p>", "foo"
);

IgnoreAttributeComparer.Compare(comparison, CompareResult.Different).ShouldBe(CompareResult.Different);
IgnoreAttributeComparer.Compare(comparison, CompareResult.Same).ShouldBe(CompareResult.Same);
IgnoreAttributeStrategy.Compare(comparison, CompareResult.Different).ShouldBe(CompareResult.Different);
IgnoreAttributeStrategy.Compare(comparison, CompareResult.Same).ShouldBe(CompareResult.Same);
}

[Fact(DisplayName = "When a attribute does contain have the ':ignore' postfix, Same is returned")]
Expand All @@ -42,6 +42,6 @@ public void Test004()
@"<p foo=""baz""></p>", "foo"
);

IgnoreAttributeComparer.Compare(comparison, CompareResult.Unknown).ShouldBe(CompareResult.Same);
IgnoreAttributeStrategy.Compare(comparison, CompareResult.Unknown).ShouldBe(CompareResult.Same);
}
}
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(IgnoreAttributeStrategy.Match, StrategyType.Generalized);
return builder;
}

Expand All @@ -33,7 +34,7 @@ public static IDiffingStrategyCollection AddAttributeComparer(this IDiffingStrat
{
builder.AddMatcher(PostfixedAttributeMatcher.Match, StrategyType.Specialized);
builder.AddComparer(AttributeComparer.Compare, StrategyType.Generalized);
builder.AddComparer(IgnoreAttributeComparer.Compare, StrategyType.Specialized);
builder.AddComparer(IgnoreAttributeStrategy.Compare, StrategyType.Specialized);
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 @@ -3,6 +3,7 @@
/// <summary>
/// Represents the ignore attribute comparer.
/// </summary>
[Obsolete("Has been moved to IgnoreAttributeStrategy")]
public static class IgnoreAttributeComparer
{
private const string DIFF_IGNORE_POSTFIX = ":ignore";
Expand All @@ -15,8 +16,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;
}
}

private 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,47 @@
namespace AngleSharp.Diffing.Strategies.AttributeStrategies;

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

/// <summary>
/// The ignore attribute comparer.
/// </summary>
public static CompareResult Compare(in AttributeComparison comparison, CompareResult currentDecision)
{
if (currentDecision.IsSameOrSkip)
return currentDecision;

return IsIgnoreAttribute(comparison.Control.Attribute)
? CompareResult.Same
: currentDecision;
}

/// <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);
}
}

private static bool IsIgnoreAttribute(IAttr source)
{
return source.Name.EndsWith(DIFF_IGNORE_POSTFIX, StringComparison.OrdinalIgnoreCase);
}
}