From 57519a018eda0a94f4e8d0526da3f0c17f419e74 Mon Sep 17 00:00:00 2001 From: Rishat Gildanov Date: Sun, 5 Sep 2021 18:07:07 +0500 Subject: [PATCH 1/6] ADD: diff:ignoreChildren comparer --- .../Core/HtmlDifferenceEngineTest.cs | 17 ++++ .../IgnoreChildrenElementComparerTest.cs | 78 +++++++++++++++++++ src/AngleSharp.Diffing/Core/CompareResult.cs | 15 +++- .../Core/HtmlDifferenceEngine.cs | 7 +- ...iffingStrategyPipelineBuilderExtensions.cs | 2 +- ...iffingStrategyPipelineBuilderExtensions.cs | 11 +++ .../IgnoreChildrenElementComparer.cs | 34 ++++++++ 7 files changed, 156 insertions(+), 8 deletions(-) create mode 100644 src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs create mode 100644 src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreChildrenElementComparer.cs diff --git a/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs b/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs index 302f713..181aa1d 100644 --- a/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs +++ b/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs @@ -357,6 +357,23 @@ public void Test2() results.ShouldBeEmpty(); } + [Fact(DisplayName = "When comparer returns SkipChildren from an element comparison, the attributes are compared but child nodes not compared")] + public void Test3() + { + var sut = CreateHtmlDiffer( + nodeMatcher: OneToOneNodeListMatcher, + nodeFilter: NoneNodeFilter, + nodeComparer: c => c.Control.Node.NodeName == "P" ? CompareResult.Same | CompareResult.SkipChildren : throw new Exception("NODE COMPARER SHOULD NOT BE CALLED ON CHILD NODES"), + attrMatcher: AttributeNameMatcher, + attrFilter: NoneAttrFilter, + attrComparer: SameResultAttrComparer + ); + + var results = sut.Compare(ToNodeList(@"

foo

"), ToNodeList(@"

baz

")); + + results.ShouldBeEmpty(); + } + #region NodeFilters private static FilterDecision NoneNodeFilter(ComparisonSource source) => FilterDecision.Keep; private static FilterDecision RemoveCommentNodeFilter(ComparisonSource source) => source.Node.NodeType == NodeType.Comment ? FilterDecision.Exclude : FilterDecision.Keep; diff --git a/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs b/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs new file mode 100644 index 0000000..cc4a347 --- /dev/null +++ b/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs @@ -0,0 +1,78 @@ +using System.Linq; + +using AngleSharp.Diffing.Core; + +using Shouldly; + +using Xunit; + +namespace AngleSharp.Diffing.Strategies.ElementStrategies +{ + public class IgnoreChildrenElementComparerTest : DiffingTestBase + { + public IgnoreChildrenElementComparerTest(DiffingTestFixture fixture) : base(fixture) + { + } + + [Theory(DisplayName = "When a control element does not contain the 'diff:ignoreChildren' attribute or it is 'diff:ignoreChildren=false', the current decision is returned")] + [InlineData(@"

")] + [InlineData(@"

")] + [InlineData(@"

")] + [InlineData(@"

")] + public void Test001(string controlHtml) + { + var comparison = ToComparison(controlHtml, "

"); + + IgnoreChildrenElementComparer.Compare(comparison, CompareResult.Different).ShouldBe(CompareResult.Different); + IgnoreChildrenElementComparer.Compare(comparison, CompareResult.Same).ShouldBe(CompareResult.Same); + IgnoreChildrenElementComparer.Compare(comparison, CompareResult.Skip).ShouldBe(CompareResult.Skip); + } + + [Theory(DisplayName = "When a control element has 'diff:ignoreChildren' attribute, SameAndBreak is returned")] + [InlineData(@"

")] + [InlineData(@"

")] + [InlineData(@"

")] + [InlineData(@"

")] + public void Test002(string controlHtml) + { + var comparison = ToComparison(controlHtml, "

"); + + IgnoreChildrenElementComparer.Compare(comparison, CompareResult.Same).ShouldBe(CompareResult.Same | CompareResult.SkipChildren); + IgnoreChildrenElementComparer.Compare(comparison, CompareResult.Different).ShouldBe(CompareResult.Different | CompareResult.SkipChildren); + } + + [Fact(DisplayName = "When a control element has 'diff:ignoreChildren', calling Build() with DefaultOptions() returns expected diffs")] + public void Test003() + { + var control = @"

hello world

"; + var test = @"

world says hello

"; + + var diffs = DiffBuilder + .Compare(control) + .WithTest(test) + .Build() + .ToList(); + + diffs.Count.ShouldBe(3); + diffs.SingleOrDefault(x => x is AttrDiff).ShouldNotBeNull(); + diffs.SingleOrDefault(x => x is MissingAttrDiff).ShouldNotBeNull(); + diffs.SingleOrDefault(x => x is UnexpectedAttrDiff).ShouldNotBeNull(); + } + + [Theory(DisplayName = "When a control element has 'diff:ignoreChildren', calling Build() with DefaultOptions() returns empty diffs")] + [InlineData(@"

hello world

", + @"

world says hello

")] + [InlineData(@"

hello world

", + @"

world says hello

")] + public void Test004(string control, string test) + { + var diffs = DiffBuilder + .Compare(control) + .WithTest(test) + .Build() + .ToList(); + + diffs.ShouldBeEmpty(); + } + } +} diff --git a/src/AngleSharp.Diffing/Core/CompareResult.cs b/src/AngleSharp.Diffing/Core/CompareResult.cs index 4ec46ea..5cd5931 100644 --- a/src/AngleSharp.Diffing/Core/CompareResult.cs +++ b/src/AngleSharp.Diffing/Core/CompareResult.cs @@ -1,22 +1,29 @@ -namespace AngleSharp.Diffing.Core +using System; + +namespace AngleSharp.Diffing.Core { /// /// Represents a result of a comparison. /// + [Flags] public enum CompareResult { /// /// Use when the two compared nodes or attributes are the same. /// - Same, + Same = 1, /// /// Use when the two compared nodes or attributes are the different. /// - Different, + Different = 2, /// /// Use when the comparison should be skipped and any child-nodes or attributes skipped as well. /// - Skip + Skip = 4, + /// + /// Use when the comparison should skip any child-nodes. + /// + SkipChildren = 8, } /// diff --git a/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs b/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs index e4b6a2d..42595a9 100644 --- a/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs +++ b/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs @@ -92,7 +92,7 @@ private IEnumerable CompareNode(in Comparison comparison) } var compareRes = _diffingStrategy.Compare(comparison); - if (compareRes == CompareResult.Different) + if (compareRes.HasFlag(CompareResult.Different)) { IDiff diff = new NodeDiff(comparison); return new[] { diff }; @@ -106,7 +106,7 @@ private IEnumerable CompareElement(in Comparison comparison) var result = new List(); var compareRes = _diffingStrategy.Compare(comparison); - if (compareRes == CompareResult.Different) + if (compareRes.HasFlag(CompareResult.Different)) { result.Add(new NodeDiff(comparison)); } @@ -114,7 +114,8 @@ private IEnumerable CompareElement(in Comparison comparison) if (compareRes != CompareResult.Skip) { result.AddRange(CompareElementAttributes(comparison)); - result.AddRange(CompareChildNodes(comparison)); + if (!compareRes.HasFlag(CompareResult.SkipChildren)) + result.AddRange(CompareChildNodes(comparison)); } return result; diff --git a/src/AngleSharp.Diffing/Strategies/DiffingStrategyPipelineBuilderExtensions.cs b/src/AngleSharp.Diffing/Strategies/DiffingStrategyPipelineBuilderExtensions.cs index 3bd094a..d65c468 100644 --- a/src/AngleSharp.Diffing/Strategies/DiffingStrategyPipelineBuilderExtensions.cs +++ b/src/AngleSharp.Diffing/Strategies/DiffingStrategyPipelineBuilderExtensions.cs @@ -19,13 +19,13 @@ public static IDiffingStrategyCollection AddDefaultOptions(this IDiffingStrategy .AddAttributeNameMatcher() .AddElementComparer() .AddIgnoreElementSupport() + .AddIgnoreChildrenElementSupport() .AddStyleSheetComparer() .AddTextComparer(WhitespaceOption.Normalize, ignoreCase: false) .AddAttributeComparer() .AddClassAttributeComparer() .AddBooleanAttributeComparer(BooleanAttributeComparision.Strict) .AddStyleAttributeComparer(); - ; } } } diff --git a/src/AngleSharp.Diffing/Strategies/ElementStrategies/DiffingStrategyPipelineBuilderExtensions.cs b/src/AngleSharp.Diffing/Strategies/ElementStrategies/DiffingStrategyPipelineBuilderExtensions.cs index a853542..fa0c06a 100644 --- a/src/AngleSharp.Diffing/Strategies/ElementStrategies/DiffingStrategyPipelineBuilderExtensions.cs +++ b/src/AngleSharp.Diffing/Strategies/ElementStrategies/DiffingStrategyPipelineBuilderExtensions.cs @@ -33,5 +33,16 @@ public static IDiffingStrategyCollection AddIgnoreElementSupport(this IDiffingSt builder.AddComparer(IgnoreElementComparer.Compare, StrategyType.Specialized); return builder; } + + /// + /// Enables the ignore children element `diff:ignorechildren` attribute during diffing. + /// + /// + /// + public static IDiffingStrategyCollection AddIgnoreChildrenElementSupport(this IDiffingStrategyCollection builder) + { + builder.AddComparer(IgnoreChildrenElementComparer.Compare, StrategyType.Specialized); + return builder; + } } } diff --git a/src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreChildrenElementComparer.cs b/src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreChildrenElementComparer.cs new file mode 100644 index 0000000..8df2658 --- /dev/null +++ b/src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreChildrenElementComparer.cs @@ -0,0 +1,34 @@ +using AngleSharp.Diffing.Core; +using AngleSharp.Diffing.Extensions; +using AngleSharp.Dom; + +namespace AngleSharp.Diffing.Strategies.ElementStrategies +{ + /// + /// Represents the ignore children element comparer. + /// + public static class IgnoreChildrenElementComparer + { + private const string DIFF_IGNORE_CHILDREN_ATTRIBUTE = "diff:ignorechildren"; + + /// + /// The ignore children element comparer. + /// + public static CompareResult Compare(in Comparison comparison, CompareResult currentDecision) + { + if (currentDecision == CompareResult.Skip) + return currentDecision; + + return ControlHasTruthyIgnoreAttribute(comparison) + ? currentDecision | CompareResult.SkipChildren + : currentDecision; + } + + private static bool ControlHasTruthyIgnoreAttribute(in Comparison comparison) + { + return comparison.Control.Node is IElement element && + element.TryGetAttrValue(DIFF_IGNORE_CHILDREN_ATTRIBUTE, out bool shouldIgnore) && + shouldIgnore; + } + } +} From 067647276ba63192e3bbc68abd47bf96fba319d7 Mon Sep 17 00:00:00 2001 From: Rishat Gildanov Date: Tue, 7 Sep 2021 09:54:19 +0500 Subject: [PATCH 2/6] Fix and simplify IgnoreChildrenElementComparerTest --- .../IgnoreChildrenElementComparerTest.cs | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs b/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs index cc4a347..387223a 100644 --- a/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs +++ b/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs @@ -28,7 +28,7 @@ public void Test001(string controlHtml) IgnoreChildrenElementComparer.Compare(comparison, CompareResult.Skip).ShouldBe(CompareResult.Skip); } - [Theory(DisplayName = "When a control element has 'diff:ignoreChildren' attribute, SameAndBreak is returned")] + [Theory(DisplayName = "When a control element has 'diff:ignoreChildren' attribute, CompareResult.SkipChildren flag is returned")] [InlineData(@"

")] [InlineData(@"

")] [InlineData(@"

")] @@ -41,29 +41,13 @@ public void Test002(string controlHtml) IgnoreChildrenElementComparer.Compare(comparison, CompareResult.Different).ShouldBe(CompareResult.Different | CompareResult.SkipChildren); } - [Fact(DisplayName = "When a control element has 'diff:ignoreChildren', calling Build() with DefaultOptions() returns expected diffs")] - public void Test003() - { - var control = @"

hello world

"; - var test = @"

world says hello

"; - - var diffs = DiffBuilder - .Compare(control) - .WithTest(test) - .Build() - .ToList(); - - diffs.Count.ShouldBe(3); - diffs.SingleOrDefault(x => x is AttrDiff).ShouldNotBeNull(); - diffs.SingleOrDefault(x => x is MissingAttrDiff).ShouldNotBeNull(); - diffs.SingleOrDefault(x => x is UnexpectedAttrDiff).ShouldNotBeNull(); - } - [Theory(DisplayName = "When a control element has 'diff:ignoreChildren', calling Build() with DefaultOptions() returns empty diffs")] - [InlineData(@"

hello world

", - @"

world says hello

")] - [InlineData(@"

hello world

", - @"

world says hello

")] + [InlineData(@"

hello world

", + @"

world says hello

")] + [InlineData(@"

hello

", + @"

world says hello

")] + [InlineData(@"

hello world

", + @"

world says

")] public void Test004(string control, string test) { var diffs = DiffBuilder From e0c50923a8a5e286234d5ec1832387a5d3300529 Mon Sep 17 00:00:00 2001 From: Rishat Gildanov Date: Tue, 7 Sep 2021 10:09:39 +0500 Subject: [PATCH 3/6] Fix SkipChildren tests --- src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs b/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs index 181aa1d..ecaa552 100644 --- a/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs +++ b/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs @@ -357,7 +357,7 @@ public void Test2() results.ShouldBeEmpty(); } - [Fact(DisplayName = "When comparer returns SkipChildren from an element comparison, the attributes are compared but child nodes not compared")] + [Fact(DisplayName = "When comparer returns SkipChildren from an element comparison, child nodes are not compared")] public void Test3() { var sut = CreateHtmlDiffer( @@ -369,7 +369,7 @@ public void Test3() attrComparer: SameResultAttrComparer ); - var results = sut.Compare(ToNodeList(@"

foo

"), ToNodeList(@"

baz

")); + var results = sut.Compare(ToNodeList(@"

foo

"), ToNodeList(@"

baz

")); results.ShouldBeEmpty(); } From a8263ce39a604e946da218bf1d6ecc0afb68d6c2 Mon Sep 17 00:00:00 2001 From: Rishat Gildanov Date: Tue, 7 Sep 2021 11:11:24 +0500 Subject: [PATCH 4/6] ADD: diff:ignoreAttributes element comparer --- .../Core/HtmlDifferenceEngineTest.cs | 17 +++++++ .../DiffBuilderTest.cs | 36 +++++++++++++++ .../IgnoreAttributesElementComparerTest.cs | 44 +++++++++++++++++++ .../IgnoreChildrenElementComparerTest.cs | 22 +--------- src/AngleSharp.Diffing/Core/CompareResult.cs | 4 ++ .../Core/HtmlDifferenceEngine.cs | 3 +- ...iffingStrategyPipelineBuilderExtensions.cs | 6 ++- ...iffingStrategyPipelineBuilderExtensions.cs | 13 +++++- .../IgnoreAttributesElementComparer.cs | 34 ++++++++++++++ .../IgnoreChildrenElementComparer.cs | 4 +- 10 files changed, 157 insertions(+), 26 deletions(-) create mode 100644 src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreAttributesElementComparerTest.cs create mode 100644 src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreAttributesElementComparer.cs diff --git a/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs b/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs index ecaa552..1c6e699 100644 --- a/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs +++ b/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs @@ -374,6 +374,23 @@ public void Test3() results.ShouldBeEmpty(); } + [Fact(DisplayName = "When comparer returns SkipAttributes from an element comparison, attributes are not compared")] + public void Test4() + { + var sut = CreateHtmlDiffer( + nodeMatcher: OneToOneNodeListMatcher, + nodeFilter: NoneNodeFilter, + nodeComparer: c => CompareResult.Same | CompareResult.SkipAttributes, + attrMatcher: AttributeNameMatcher, + attrFilter: NoneAttrFilter, + attrComparer: SameResultAttrComparer + ); + + var results = sut.Compare(ToNodeList(@"

"), ToNodeList(@"

")); + + results.ShouldBeEmpty(); + } + #region NodeFilters private static FilterDecision NoneNodeFilter(ComparisonSource source) => FilterDecision.Keep; private static FilterDecision RemoveCommentNodeFilter(ComparisonSource source) => source.Node.NodeType == NodeType.Comment ? FilterDecision.Exclude : FilterDecision.Keep; diff --git a/src/AngleSharp.Diffing.Tests/DiffBuilderTest.cs b/src/AngleSharp.Diffing.Tests/DiffBuilderTest.cs index 8d973c2..be8914f 100644 --- a/src/AngleSharp.Diffing.Tests/DiffBuilderTest.cs +++ b/src/AngleSharp.Diffing.Tests/DiffBuilderTest.cs @@ -89,5 +89,41 @@ public void Test004() nodeComparerCalled.ShouldBeTrue(); attrComparerCalled.ShouldBeTrue(); } + + [Theory(DisplayName = "When a control element has 'diff:ignoreChildren', calling Build() with DefaultOptions() returns empty diffs")] + [InlineData(@"

hello world

", + @"

world says hello

")] + [InlineData(@"

hello

", + @"

world says hello

")] + [InlineData(@"

hello world

", + @"

world says

")] + public void Test005(string control, string test) + { + var diffs = DiffBuilder + .Compare(control) + .WithTest(test) + .Build() + .ToList(); + + diffs.ShouldBeEmpty(); + } + + [Theory(DisplayName = "When a control element has 'diff:ignoreAttributes', calling Build() with DefaultOptions() returns empty diffs")] + [InlineData(@"

", + @"

")] + [InlineData(@"

", + @"

")] + [InlineData(@"

", + @"

")] + public void Test006(string control, string test) + { + var diffs = DiffBuilder + .Compare(control) + .WithTest(test) + .Build() + .ToList(); + + diffs.ShouldBeEmpty(); + } } } diff --git a/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreAttributesElementComparerTest.cs b/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreAttributesElementComparerTest.cs new file mode 100644 index 0000000..6959de7 --- /dev/null +++ b/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreAttributesElementComparerTest.cs @@ -0,0 +1,44 @@ +using System.Linq; + +using AngleSharp.Diffing.Core; + +using Shouldly; + +using Xunit; + +namespace AngleSharp.Diffing.Strategies.ElementStrategies +{ + public class IgnoreAttributesElementComparerTest : DiffingTestBase + { + public IgnoreAttributesElementComparerTest(DiffingTestFixture fixture) : base(fixture) + { + } + + [Theory(DisplayName = "When a control element does not contain the 'diff:ignoreAttributes' attribute or it is 'diff:ignoreAttributes=false', the current decision is returned")] + [InlineData(@"

")] + [InlineData(@"

")] + [InlineData(@"

")] + [InlineData(@"

")] + public void Test001(string controlHtml) + { + var comparison = ToComparison(controlHtml, "

"); + + IgnoreAttributesElementComparer.Compare(comparison, CompareResult.Different).ShouldBe(CompareResult.Different); + IgnoreAttributesElementComparer.Compare(comparison, CompareResult.Same).ShouldBe(CompareResult.Same); + IgnoreAttributesElementComparer.Compare(comparison, CompareResult.Skip).ShouldBe(CompareResult.Skip); + } + + [Theory(DisplayName = "When a control element has 'diff:ignoreAttributes' attribute, CompareResult.SkipAttributes flag is returned")] + [InlineData(@"

")] + [InlineData(@"

")] + [InlineData(@"

")] + [InlineData(@"

")] + public void Test002(string controlHtml) + { + var comparison = ToComparison(controlHtml, "

"); + + IgnoreAttributesElementComparer.Compare(comparison, CompareResult.Same).ShouldBe(CompareResult.Same | CompareResult.SkipAttributes); + IgnoreAttributesElementComparer.Compare(comparison, CompareResult.Different).ShouldBe(CompareResult.Different | CompareResult.SkipAttributes); + } + } +} diff --git a/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs b/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs index 387223a..c02e7b0 100644 --- a/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs +++ b/src/AngleSharp.Diffing.Tests/Strategies/ElementStrategies/IgnoreChildrenElementComparerTest.cs @@ -21,7 +21,7 @@ public IgnoreChildrenElementComparerTest(DiffingTestFixture fixture) : base(fixt [InlineData(@"

")] public void Test001(string controlHtml) { - var comparison = ToComparison(controlHtml, "

"); + var comparison = ToComparison(controlHtml, "

"); IgnoreChildrenElementComparer.Compare(comparison, CompareResult.Different).ShouldBe(CompareResult.Different); IgnoreChildrenElementComparer.Compare(comparison, CompareResult.Same).ShouldBe(CompareResult.Same); @@ -35,28 +35,10 @@ public void Test001(string controlHtml) [InlineData(@"

")] public void Test002(string controlHtml) { - var comparison = ToComparison(controlHtml, "

"); + var comparison = ToComparison(controlHtml, "

"); IgnoreChildrenElementComparer.Compare(comparison, CompareResult.Same).ShouldBe(CompareResult.Same | CompareResult.SkipChildren); IgnoreChildrenElementComparer.Compare(comparison, CompareResult.Different).ShouldBe(CompareResult.Different | CompareResult.SkipChildren); } - - [Theory(DisplayName = "When a control element has 'diff:ignoreChildren', calling Build() with DefaultOptions() returns empty diffs")] - [InlineData(@"

hello world

", - @"

world says hello

")] - [InlineData(@"

hello

", - @"

world says hello

")] - [InlineData(@"

hello world

", - @"

world says

")] - public void Test004(string control, string test) - { - var diffs = DiffBuilder - .Compare(control) - .WithTest(test) - .Build() - .ToList(); - - diffs.ShouldBeEmpty(); - } } } diff --git a/src/AngleSharp.Diffing/Core/CompareResult.cs b/src/AngleSharp.Diffing/Core/CompareResult.cs index 5cd5931..7b5ea20 100644 --- a/src/AngleSharp.Diffing/Core/CompareResult.cs +++ b/src/AngleSharp.Diffing/Core/CompareResult.cs @@ -24,6 +24,10 @@ public enum CompareResult /// Use when the comparison should skip any child-nodes. ///
SkipChildren = 8, + /// + /// Use when the comparison should skip any attributes. + /// + SkipAttributes = 16, } /// diff --git a/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs b/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs index 42595a9..4cd0282 100644 --- a/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs +++ b/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs @@ -113,7 +113,8 @@ private IEnumerable CompareElement(in Comparison comparison) if (compareRes != CompareResult.Skip) { - result.AddRange(CompareElementAttributes(comparison)); + if (!compareRes.HasFlag(CompareResult.SkipAttributes)) + result.AddRange(CompareElementAttributes(comparison)); if (!compareRes.HasFlag(CompareResult.SkipChildren)) result.AddRange(CompareChildNodes(comparison)); } diff --git a/src/AngleSharp.Diffing/Strategies/DiffingStrategyPipelineBuilderExtensions.cs b/src/AngleSharp.Diffing/Strategies/DiffingStrategyPipelineBuilderExtensions.cs index d65c468..9454b08 100644 --- a/src/AngleSharp.Diffing/Strategies/DiffingStrategyPipelineBuilderExtensions.cs +++ b/src/AngleSharp.Diffing/Strategies/DiffingStrategyPipelineBuilderExtensions.cs @@ -19,13 +19,15 @@ public static IDiffingStrategyCollection AddDefaultOptions(this IDiffingStrategy .AddAttributeNameMatcher() .AddElementComparer() .AddIgnoreElementSupport() - .AddIgnoreChildrenElementSupport() .AddStyleSheetComparer() .AddTextComparer(WhitespaceOption.Normalize, ignoreCase: false) .AddAttributeComparer() .AddClassAttributeComparer() .AddBooleanAttributeComparer(BooleanAttributeComparision.Strict) - .AddStyleAttributeComparer(); + .AddStyleAttributeComparer() + .AddIgnoreChildrenElementSupport() + .AddIgnoreAttributesElementSupport() + ; } } } diff --git a/src/AngleSharp.Diffing/Strategies/ElementStrategies/DiffingStrategyPipelineBuilderExtensions.cs b/src/AngleSharp.Diffing/Strategies/ElementStrategies/DiffingStrategyPipelineBuilderExtensions.cs index fa0c06a..3b0b475 100644 --- a/src/AngleSharp.Diffing/Strategies/ElementStrategies/DiffingStrategyPipelineBuilderExtensions.cs +++ b/src/AngleSharp.Diffing/Strategies/ElementStrategies/DiffingStrategyPipelineBuilderExtensions.cs @@ -35,7 +35,7 @@ public static IDiffingStrategyCollection AddIgnoreElementSupport(this IDiffingSt } /// - /// Enables the ignore children element `diff:ignorechildren` attribute during diffing. + /// Enables the ignore children element `diff:ignoreChildren` attribute during diffing. /// /// /// @@ -44,5 +44,16 @@ public static IDiffingStrategyCollection AddIgnoreChildrenElementSupport(this ID builder.AddComparer(IgnoreChildrenElementComparer.Compare, StrategyType.Specialized); return builder; } + + /// + /// Enables the ignore attributes element `diff:ignoreAttributes` attribute during diffing. + /// + /// + /// + public static IDiffingStrategyCollection AddIgnoreAttributesElementSupport(this IDiffingStrategyCollection builder) + { + builder.AddComparer(IgnoreAttributesElementComparer.Compare, StrategyType.Specialized); + return builder; + } } } diff --git a/src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreAttributesElementComparer.cs b/src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreAttributesElementComparer.cs new file mode 100644 index 0000000..7dc682a --- /dev/null +++ b/src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreAttributesElementComparer.cs @@ -0,0 +1,34 @@ +using AngleSharp.Diffing.Core; +using AngleSharp.Diffing.Extensions; +using AngleSharp.Dom; + +namespace AngleSharp.Diffing.Strategies.ElementStrategies +{ + /// + /// Represents the ignore attributes element comparer. + /// + public static class IgnoreAttributesElementComparer + { + private const string DIFF_IGNORE_ATTRIBUTES_ATTRIBUTE = "diff:ignoreattributes"; + + /// + /// The ignore attributes element comparer. + /// + public static CompareResult Compare(in Comparison comparison, CompareResult currentDecision) + { + if (currentDecision == CompareResult.Skip) + return currentDecision; + + return ControlHasTruthyIgnoreAttributesAttribute(comparison) + ? currentDecision | CompareResult.SkipAttributes + : currentDecision; + } + + private static bool ControlHasTruthyIgnoreAttributesAttribute(in Comparison comparison) + { + return comparison.Control.Node is IElement element && + element.TryGetAttrValue(DIFF_IGNORE_ATTRIBUTES_ATTRIBUTE, out bool shouldIgnore) && + shouldIgnore; + } + } +} diff --git a/src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreChildrenElementComparer.cs b/src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreChildrenElementComparer.cs index 8df2658..ce239af 100644 --- a/src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreChildrenElementComparer.cs +++ b/src/AngleSharp.Diffing/Strategies/ElementStrategies/IgnoreChildrenElementComparer.cs @@ -19,12 +19,12 @@ public static CompareResult Compare(in Comparison comparison, CompareResult curr if (currentDecision == CompareResult.Skip) return currentDecision; - return ControlHasTruthyIgnoreAttribute(comparison) + return ControlHasTruthyIgnoreChildrenAttribute(comparison) ? currentDecision | CompareResult.SkipChildren : currentDecision; } - private static bool ControlHasTruthyIgnoreAttribute(in Comparison comparison) + private static bool ControlHasTruthyIgnoreChildrenAttribute(in Comparison comparison) { return comparison.Control.Node is IElement element && element.TryGetAttrValue(DIFF_IGNORE_CHILDREN_ATTRIBUTE, out bool shouldIgnore) && From 24ec419568ec7d965aa3d7c26dc5ae3742aecdbd Mon Sep 17 00:00:00 2001 From: Rishat Gildanov Date: Wed, 8 Sep 2021 08:15:31 +0500 Subject: [PATCH 5/6] Fix HtmlDifferenceEngineTest --- .../Core/HtmlDifferenceEngineTest.cs | 16 ++++++++++------ .../Core/HtmlDifferenceEngine.cs | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs b/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs index 1c6e699..6068afb 100644 --- a/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs +++ b/src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs @@ -357,13 +357,15 @@ public void Test2() results.ShouldBeEmpty(); } - [Fact(DisplayName = "When comparer returns SkipChildren from an element comparison, child nodes are not compared")] - public void Test3() + [Theory(DisplayName = "When comparer returns SkipChildren flag from an element comparison, child nodes are not compared")] + [InlineData(CompareResult.Same | CompareResult.SkipChildren)] + [InlineData(CompareResult.Skip | CompareResult.SkipChildren)] + public void Test3(CompareResult compareResult) { var sut = CreateHtmlDiffer( nodeMatcher: OneToOneNodeListMatcher, nodeFilter: NoneNodeFilter, - nodeComparer: c => c.Control.Node.NodeName == "P" ? CompareResult.Same | CompareResult.SkipChildren : throw new Exception("NODE COMPARER SHOULD NOT BE CALLED ON CHILD NODES"), + nodeComparer: c => c.Control.Node.NodeName == "P" ? compareResult : throw new Exception("NODE COMPARER SHOULD NOT BE CALLED ON CHILD NODES"), attrMatcher: AttributeNameMatcher, attrFilter: NoneAttrFilter, attrComparer: SameResultAttrComparer @@ -374,13 +376,15 @@ public void Test3() results.ShouldBeEmpty(); } - [Fact(DisplayName = "When comparer returns SkipAttributes from an element comparison, attributes are not compared")] - public void Test4() + [Theory(DisplayName = "When comparer returns SkipAttributes flag from an element comparison, attributes are not compared")] + [InlineData(CompareResult.Same | CompareResult.SkipAttributes)] + [InlineData(CompareResult.Skip | CompareResult.SkipAttributes)] + public void Test4(CompareResult compareResult) { var sut = CreateHtmlDiffer( nodeMatcher: OneToOneNodeListMatcher, nodeFilter: NoneNodeFilter, - nodeComparer: c => CompareResult.Same | CompareResult.SkipAttributes, + nodeComparer: c => compareResult, attrMatcher: AttributeNameMatcher, attrFilter: NoneAttrFilter, attrComparer: SameResultAttrComparer diff --git a/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs b/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs index 4cd0282..16577f2 100644 --- a/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs +++ b/src/AngleSharp.Diffing/Core/HtmlDifferenceEngine.cs @@ -111,7 +111,7 @@ private IEnumerable CompareElement(in Comparison comparison) result.Add(new NodeDiff(comparison)); } - if (compareRes != CompareResult.Skip) + if (!compareRes.HasFlag(CompareResult.Skip)) { if (!compareRes.HasFlag(CompareResult.SkipAttributes)) result.AddRange(CompareElementAttributes(comparison)); From 5a95070495e91cbaa9eb9bff322d7d06684de0f2 Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Wed, 8 Sep 2021 05:41:51 +0000 Subject: [PATCH 6/6] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0721ce5..068a2d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0.17.0 + +Released on Wednesday, September 8, 2021. + +- Added the ability to ignore an elements children or its attributes. By [@grishat](https://github.com/grishat). + # 0.16.0 Released on Wednesday, June 24, 2021.