Skip to content

Commit 600656d

Browse files
authored
feat: Unmatched :ignore'ed attributes does not contribute diffs
* Ignore unmatched :ignore attributes * Add more tests * Add test to cover situation where no ignore attribute was requested * Add :ignore filter approach * Fix Linux pipeline * Create IgnoreAttributeStrategy and make IgnoreAttributeComparer obsolete * Revert "Fix Linux pipeline" This reverts commit 2597558.
1 parent 4de430b commit 600656d

File tree

8 files changed

+148
-9
lines changed

8 files changed

+148
-9
lines changed

src/AngleSharp.Diffing.Tests/DiffBuilderTest.cs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
using AngleSharp.Diffing.Strategies;
2+
using AngleSharp.Diffing.Strategies.AttributeStrategies;
3+
using AngleSharp.Diffing.Strategies.TextNodeStrategies;
4+
using AngleSharp.Diffing.TestData;
5+
16
namespace AngleSharp.Diffing;
27

38

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

117122
diffs.ShouldBeEmpty();
118123
}
119-
}
124+
125+
[Theory(DisplayName =
126+
"When a control element has ':ignore', elements with and without that attribute should return empty diffs")]
127+
[MemberData(nameof(IgnoreAttributeTestData.ControlAndHtmlData), MemberType = typeof(IgnoreAttributeTestData))]
128+
public void Test007(string controlHtml, string testHtml)
129+
{
130+
var diffs = DiffBuilder.Compare(controlHtml).WithTest(testHtml).Build();
131+
Assert.Empty(diffs);
132+
}
133+
134+
[Theory(DisplayName =
135+
"When a control element has ':ignore', but IgnoreAttributeComparer is not active, diffs should be found")]
136+
[MemberData(nameof(IgnoreAttributeTestData.ControlHtmlAndDiffData), MemberType = typeof(IgnoreAttributeTestData))]
137+
public void Test008(string controlHtml, string testHtml, DiffResult expectedDiffResult)
138+
{
139+
var diffs = DiffBuilder
140+
.Compare(controlHtml)
141+
.WithTest(testHtml)
142+
.WithOptions(a => a // Most important thing to note here is we do not have a ignore attribute comparer
143+
.AddSearchingNodeMatcher()
144+
.AddMatcher(AttributeNameMatcher.Match, StrategyType.Generalized)
145+
.AddElementComparer(enforceTagClosing: false)
146+
.AddMatcher(PostfixedAttributeMatcher.Match, StrategyType.Specialized)
147+
.AddComparer(AttributeComparer.Compare, StrategyType.Generalized)
148+
.AddClassAttributeComparer()
149+
.AddBooleanAttributeComparer(BooleanAttributeComparision.Strict)
150+
.AddStyleAttributeComparer())
151+
.Build()
152+
.ToList();
153+
154+
Assert.Single(diffs);
155+
Assert.Equal(DiffTarget.Attribute, diffs[0].Target);
156+
Assert.Equal(expectedDiffResult, diffs[0].Result);
157+
}
158+
}

src/AngleSharp.Diffing.Tests/Strategies/AttributeStrategies/IgnoreAttributeComparerTest.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public void Test000(CompareResult currentResult)
1717
@"<p foo=""bar""></p>", "foo"
1818
);
1919

20-
IgnoreAttributeComparer
20+
IgnoreAttributeStrategy
2121
.Compare(comparison, currentResult)
2222
.ShouldBe(currentResult);
2323
}
@@ -30,8 +30,8 @@ public void Test003()
3030
@"<p foo=""bar""></p>", "foo"
3131
);
3232

33-
IgnoreAttributeComparer.Compare(comparison, CompareResult.Different).ShouldBe(CompareResult.Different);
34-
IgnoreAttributeComparer.Compare(comparison, CompareResult.Same).ShouldBe(CompareResult.Same);
33+
IgnoreAttributeStrategy.Compare(comparison, CompareResult.Different).ShouldBe(CompareResult.Different);
34+
IgnoreAttributeStrategy.Compare(comparison, CompareResult.Same).ShouldBe(CompareResult.Same);
3535
}
3636

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

45-
IgnoreAttributeComparer.Compare(comparison, CompareResult.Unknown).ShouldBe(CompareResult.Same);
45+
IgnoreAttributeStrategy.Compare(comparison, CompareResult.Unknown).ShouldBe(CompareResult.Same);
4646
}
4747
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
namespace AngleSharp.Diffing.TestData;
2+
3+
internal static class IgnoreAttributeTestData
4+
{
5+
public static TheoryData<string, string> ControlAndHtmlData()
6+
{
7+
var theoryData = new TheoryData<string, string>();
8+
foreach (var (controlHtml, expectedHtml, _) in TestCases)
9+
{
10+
theoryData.Add(controlHtml, expectedHtml);
11+
}
12+
13+
return theoryData;
14+
}
15+
16+
public static TheoryData<string, string, DiffResult> ControlHtmlAndDiffData()
17+
{
18+
var theoryData = new TheoryData<string, string, DiffResult>();
19+
foreach (var (controlHtml, expectedHtml, expectedDiffResult) in TestCases)
20+
{
21+
theoryData.Add(controlHtml, expectedHtml, expectedDiffResult);
22+
}
23+
24+
return theoryData;
25+
}
26+
27+
private static readonly IEnumerable<(string controlHtml, string expectedHtml, DiffResult expectedDiffResult)>
28+
TestCases =
29+
[
30+
("<div class:ignore></div>", "<div class=\"ian-fleming\"></div>", DiffResult.Different),
31+
("<div class:ignore></div>", "<div class=\"\"></div>", DiffResult.Different),
32+
("<div class:ignore></div>", "<div class></div>", DiffResult.Different),
33+
("<div class:ignore></div>", "<div></div>", DiffResult.Missing),
34+
("<input required:ignore/>", "<input required=\"required\"/>", DiffResult.Different),
35+
("<input required:ignore/>", "<input required=\"\"/>", DiffResult.Different),
36+
("<input required:ignore/>", "<input required/>", DiffResult.Different),
37+
("<input required:ignore/>", "<input/>", DiffResult.Missing),
38+
("<button onclick:ignore/></button>", "<button onclick=\"alert(1)\"></button>", DiffResult.Different),
39+
("<button onclick:ignore/></button>", "<button/></button>", DiffResult.Missing),
40+
("<a aria-disabled:ignore/></a>", "<a aria-disabled=\"true\"/></a>", DiffResult.Different),
41+
("<a aria-disabled:ignore/></a>", "<a/></a>", DiffResult.Missing),
42+
("<span style:ignore/></span>", "<span style=\"color:red;\"/></span>", DiffResult.Different),
43+
("<span style:ignore/></span>", "<span/></span>", DiffResult.Missing),
44+
];
45+
}

src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using AngleSharp.Diffing.Core.Diffs;
2+
using AngleSharp.Diffing.Strategies.AttributeStrategies;
23

34
namespace AngleSharp.Diffing.Core;
45

src/AngleSharp.Diffing/Core/SourceMap.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public IEnumerable<AttributeComparisonSource> GetUnmatched()
7070
{
7171
foreach (var source in _sources.Values)
7272
{
73-
if (!_matched.Contains(source.Attribute.Name))
73+
if (IsUnmatched(source.Attribute.Name))
7474
yield return source;
7575
}
7676
}

src/AngleSharp.Diffing/Strategies/AttributeStrategies/DiffingStrategyPipelineBuilderExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public static IDiffingStrategyCollection IgnoreDiffAttributes(this IDiffingStrat
2323
public static IDiffingStrategyCollection AddAttributeNameMatcher(this IDiffingStrategyCollection builder)
2424
{
2525
builder.AddMatcher(AttributeNameMatcher.Match, StrategyType.Generalized);
26+
builder.AddMatcher(IgnoreAttributeStrategy.Match, StrategyType.Generalized);
2627
return builder;
2728
}
2829

@@ -33,7 +34,7 @@ public static IDiffingStrategyCollection AddAttributeComparer(this IDiffingStrat
3334
{
3435
builder.AddMatcher(PostfixedAttributeMatcher.Match, StrategyType.Specialized);
3536
builder.AddComparer(AttributeComparer.Compare, StrategyType.Generalized);
36-
builder.AddComparer(IgnoreAttributeComparer.Compare, StrategyType.Specialized);
37+
builder.AddComparer(IgnoreAttributeStrategy.Compare, StrategyType.Specialized);
3738
return builder;
3839
}
3940

src/AngleSharp.Diffing/Strategies/AttributeStrategies/IgnoreAttributeComparer.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
/// <summary>
44
/// Represents the ignore attribute comparer.
55
/// </summary>
6+
[Obsolete("Has been moved to IgnoreAttributeStrategy")]
67
public static class IgnoreAttributeComparer
78
{
89
private const string DIFF_IGNORE_POSTFIX = ":ignore";
@@ -15,8 +16,13 @@ public static CompareResult Compare(in AttributeComparison comparison, CompareRe
1516
if (currentDecision.IsSameOrSkip)
1617
return currentDecision;
1718

18-
return comparison.Control.Attribute.Name.EndsWith(DIFF_IGNORE_POSTFIX, StringComparison.OrdinalIgnoreCase)
19+
return IsIgnoreAttribute(comparison.Control.Attribute)
1920
? CompareResult.Same
2021
: currentDecision;
2122
}
22-
}
23+
24+
private static bool IsIgnoreAttribute(IAttr source)
25+
{
26+
return source.Name.EndsWith(DIFF_IGNORE_POSTFIX, StringComparison.OrdinalIgnoreCase);
27+
}
28+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
namespace AngleSharp.Diffing.Strategies.AttributeStrategies;
2+
3+
/// <summary>
4+
/// Ignore Attribute matcher strategy.
5+
/// </summary>
6+
public static class IgnoreAttributeStrategy
7+
{
8+
private const string DIFF_IGNORE_POSTFIX = ":ignore";
9+
10+
/// <summary>
11+
/// The ignore attribute comparer.
12+
/// </summary>
13+
public static CompareResult Compare(in AttributeComparison comparison, CompareResult currentDecision)
14+
{
15+
if (currentDecision.IsSameOrSkip)
16+
return currentDecision;
17+
18+
return IsIgnoreAttribute(comparison.Control.Attribute)
19+
? CompareResult.Same
20+
: currentDecision;
21+
}
22+
23+
/// <summary>
24+
/// Attribute name matcher strategy.
25+
/// </summary>
26+
public static IEnumerable<AttributeComparison> Match(IDiffContext context, SourceMap controlSources, SourceMap testSources)
27+
{
28+
if (controlSources is null)
29+
throw new ArgumentNullException(nameof(controlSources));
30+
if (testSources is null)
31+
throw new ArgumentNullException(nameof(testSources));
32+
33+
foreach (var control in controlSources.GetUnmatched())
34+
{
35+
// An unmatched :ignore attribute can just be matched with itself if it isn't
36+
// matched with a "test" attribute of the same name already.
37+
// this means an ignored attribute is ignored even if it does not appear in the test html.
38+
if (control.Attribute.Name.EndsWith(DIFF_IGNORE_POSTFIX, StringComparison.OrdinalIgnoreCase))
39+
yield return new AttributeComparison(control, control);
40+
}
41+
}
42+
43+
private static bool IsIgnoreAttribute(IAttr source)
44+
{
45+
return source.Name.EndsWith(DIFF_IGNORE_POSTFIX, StringComparison.OrdinalIgnoreCase);
46+
}
47+
}

0 commit comments

Comments
 (0)