Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.0.0 release #44

Closed
wants to merge 6 commits into from
Closed
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# 1.0.0

- Enabled using `diff:ignoreAttributes` and `diff:ignoreChildren` together on the same element.
- Corrected list of attributes that is considered as boolean attributes. Removed `hidden`, added `inert`, `playsinline`, `shadowrootclonable`, `shadowrootdelegatesfocus`, and `shadowrootserializable`.
- Upgrade to v1.x of AngleSharp.

# 0.18.2

- Changed `CompareStrategy` such that it now can control the `IDiff` type that should be returned in case a difference is found in a comparison. This allows a comparer to embed additional context in the `IDiff` object. By [@SebastianStehle](https://github.com/SebastianStehle).
- Changed `ElementComparer` to skip comparing two nodes of different types. By [@SebastianStehle](https://github.com/SebastianStehle).

# 0.18.1

- Fixed element comparer such that it can strictly check if the closing tags in the source markup is the same.
Expand Down
3 changes: 3 additions & 0 deletions src/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,6 @@ dotnet_naming_style.fields_begin_with__.required_prefix = _
dotnet_naming_style.fields_begin_with__.required_suffix =
dotnet_naming_style.fields_begin_with__.word_separator =
dotnet_naming_style.fields_begin_with__.capitalization = camel_case

# MA0012: Do not raise reserved exception type
dotnet_diagnostic.MA0012.severity = none
14 changes: 7 additions & 7 deletions src/AngleSharp.Diffing.Tests/AngleSharp.DiffingTests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<AssemblyName>AngleSharp.Diffing.Tests</AssemblyName>
<RootNamespace>AngleSharp.Diffing</RootNamespace>
Expand All @@ -10,18 +10,18 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="GitHubActionsTestLogger" Version="2.0.1">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="GitHubActionsTestLogger" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Shouldly" Version="4.1.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PackageReference Include="Shouldly" Version="4.2.1" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
4 changes: 2 additions & 2 deletions src/AngleSharp.Diffing.Tests/Core/AttributeComparisonTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void Test005()
var test = ToAttributeComparisonSource(@"<br foo=""bar"">", "foo");
var comparison = new AttributeComparison(control, test);

var (actualCtrlElm, actualTestElm) = comparison.GetAttributeElements();
var (actualCtrlElm, actualTestElm) = comparison.AttributeElements;

actualCtrlElm.ShouldBe(control.ElementSource.Node);
actualTestElm.ShouldBe(test.ElementSource.Node);
Expand Down Expand Up @@ -135,7 +135,7 @@ public void Test005()
var test = ToAttributeComparisonSource(@"<br foo=""bar"">", "foo");
var comparison = new AttributeComparison(control, test);

var (actualCtrlElm, actualTestElm) = comparison.GetAttributeElements();
var (actualCtrlElm, actualTestElm) = comparison.AttributeElements;

actualCtrlElm.ShouldBe(control.ElementSource.Node);
actualTestElm.ShouldBe(test.ElementSource.Node);
Expand Down
4 changes: 2 additions & 2 deletions src/AngleSharp.Diffing.Tests/Core/DiffingEngineTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace AngleSharp.Diffing.Core;

public abstract class DiffingEngineTestBase : DiffingTestBase
{
public DiffingEngineTestBase(DiffingTestFixture fixture) : base(fixture)
protected DiffingEngineTestBase(DiffingTestFixture fixture) : base(fixture)
{
}

Expand All @@ -24,7 +24,7 @@ protected static HtmlDiffer CreateHtmlDiffer(
);
}

private class MockDiffingStrategy : IDiffingStrategy
private sealed class MockDiffingStrategy : IDiffingStrategy
{
private readonly Func<ComparisonSource, FilterDecision>? _nodeFilter;
private readonly Func<AttributeComparisonSource, FilterDecision>? _attrFilter;
Expand Down
113 changes: 92 additions & 21 deletions src/AngleSharp.Diffing.Tests/Core/HtmlDifferenceEngineTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,46 @@ public void WhenNodesAreDifferentADiffIsReturned()
var results = sut.Compare(nodes, nodes).ToList();

results.Count.ShouldBe(3);
results[0].ShouldBeOfType<NodeDiff>().ShouldSatisfyAllConditions(
results[0].ShouldBeAssignableTo<NodeDiff>().ShouldSatisfyAllConditions(
diff => diff.Control.Node.NodeName.ShouldBe("P"),
diff => diff.Result.ShouldBe(DiffResult.Different),
diff => diff.Target.ShouldBe(DiffTarget.Element)
);
results[1].ShouldBeOfType<NodeDiff>().ShouldSatisfyAllConditions(
results[1].ShouldBeAssignableTo<NodeDiff>().ShouldSatisfyAllConditions(
diff => diff.Control.Node.NodeName.ShouldBe("#comment"),
diff => diff.Result.ShouldBe(DiffResult.Different),
diff => diff.Target.ShouldBe(DiffTarget.Comment)
);
results[2].ShouldBeOfType<NodeDiff>().ShouldSatisfyAllConditions(
results[2].ShouldBeAssignableTo<NodeDiff>().ShouldSatisfyAllConditions(
diff => diff.Control.Node.NodeName.ShouldBe("#text"),
diff => diff.Result.ShouldBe(DiffResult.Different),
diff => diff.Target.ShouldBe(DiffTarget.Text)
);
}

[Fact(DisplayName = "When matched control/test nodes are different, a custom diff is returned")]
public void WhenNodesAreDifferentADiffIsReturnedWithCustomDiff()
{
var nodes = ToNodeList("<p></p><!--comment-->textnode");
var sut = CreateHtmlDiffer(
nodeMatcher: OneToOneNodeListMatcher,
nodeFilter: NoneNodeFilter,
nodeComparer: DiffResultCustomNodeComparer);

var results = sut.Compare(nodes, nodes).ToList();

results.Count.ShouldBe(3);
results[0].ShouldBeOfType<CustomNodeDiff>().ShouldSatisfyAllConditions(
diff => diff.Control.Node.NodeName.ShouldBe("P"),
diff => diff.Result.ShouldBe(DiffResult.Different),
diff => diff.Target.ShouldBe(DiffTarget.Element)
);
results[1].ShouldBeOfType<CustomNodeDiff>().ShouldSatisfyAllConditions(
diff => diff.Control.Node.NodeName.ShouldBe("#comment"),
diff => diff.Result.ShouldBe(DiffResult.Different),
diff => diff.Target.ShouldBe(DiffTarget.Comment)
);
results[2].ShouldBeOfType<CustomNodeDiff>().ShouldSatisfyAllConditions(
diff => diff.Control.Node.NodeName.ShouldBe("#text"),
diff => diff.Result.ShouldBe(DiffResult.Different),
diff => diff.Target.ShouldBe(DiffTarget.Text)
Expand Down Expand Up @@ -237,6 +266,30 @@ public void WhenMatchedAttrsAreDiffAttrDiffIsReturned()
);
}

[Fact(DisplayName = "When matched control/test attributes are different, a diff is returned with custom diff")]
public void WhenMatchedAttrsAreDiffAttrDiffIsReturnedWithCustomDiff()
{
var nodes = ToNodeList(@"<p id=""foo""></p>");

var sut = CreateHtmlDiffer(
nodeMatcher: OneToOneNodeListMatcher,
nodeFilter: NoneNodeFilter,
nodeComparer: SameResultNodeComparer,
attrMatcher: AttributeNameMatcher,
attrFilter: NoneAttrFilter,
attrComparer: DiffResultCustomAttrComparer);

var results = sut.Compare(nodes, nodes).ToList();

results.Count.ShouldBe(1);
results[0].ShouldBeOfType<CustomAttrDiff>().ShouldSatisfyAllConditions(
diff => diff.Control.Attribute.Name.ShouldBe("id"),
diff => diff.Test.Attribute.Name.ShouldBe("id"),
diff => diff.Result.ShouldBe(DiffResult.Different),
diff => diff.Target.ShouldBe(DiffTarget.Attribute)
);
}

[Fact(DisplayName = "When matched control/test attributes are the same, no diffs are returned")]
public void WhenMatchedAttrsAreSameNoDiffIsReturned()
{
Expand Down Expand Up @@ -268,11 +321,11 @@ public void WhenBothTestAndControlHaveChildNodesTheseAreCompared()
var results = sut.Compare(nodes, nodes).ToList();

results.Count.ShouldBe(5);
results[0].ShouldBeOfType<NodeDiff>().Control.Node.NodeName.ShouldBe("MAIN");
results[1].ShouldBeOfType<NodeDiff>().Control.Node.NodeName.ShouldBe("H1");
results[2].ShouldBeOfType<NodeDiff>().Control.Node.NodeValue.ShouldBe("foobar");
results[3].ShouldBeOfType<NodeDiff>().Control.Node.NodeName.ShouldBe("P");
results[4].ShouldBeOfType<NodeDiff>().Control.Node.NodeName.ShouldBe("#text");
results[0].ShouldBeAssignableTo<NodeDiff>().Control.Node.NodeName.ShouldBe("MAIN");
results[1].ShouldBeAssignableTo<NodeDiff>().Control.Node.NodeName.ShouldBe("H1");
results[2].ShouldBeAssignableTo<NodeDiff>().Control.Node.NodeValue.ShouldBe("foobar");
results[3].ShouldBeAssignableTo<NodeDiff>().Control.Node.NodeName.ShouldBe("P");
results[4].ShouldBeAssignableTo<NodeDiff>().Control.Node.NodeName.ShouldBe("#text");
}

[Theory(DisplayName = "When only one of the control or test node in a comparison has child nodes, a missing/unexpected diff is returned")]
Expand All @@ -288,7 +341,7 @@ public void OnlyOnePartHasChildNodes(string control, string test, Type expectedD
var results = sut.Compare(ToNodeList(control), ToNodeList(test)).ToList();

results.Count.ShouldBe(2);
results[0].ShouldBeOfType<NodeDiff>();
results[0].ShouldBeAssignableTo<NodeDiff>();
results[1].ShouldBeOfType(expectedDiffType);
}

Expand All @@ -309,8 +362,8 @@ public void ComparisonSourcesHaveCorrectType()

results.Count.ShouldBe(2);

results[0].ShouldBeOfType<NodeDiff>().Control.SourceType.ShouldBe(ComparisonSourceType.Control);
results[0].ShouldBeOfType<NodeDiff>().Test.SourceType.ShouldBe(ComparisonSourceType.Test);
results[0].ShouldBeAssignableTo<NodeDiff>().Control.SourceType.ShouldBe(ComparisonSourceType.Control);
results[0].ShouldBeAssignableTo<NodeDiff>().Test.SourceType.ShouldBe(ComparisonSourceType.Test);
results[1].ShouldBeOfType<AttrDiff>().Control.SourceType.ShouldBe(ComparisonSourceType.Control);
results[1].ShouldBeOfType<AttrDiff>().Test.SourceType.ShouldBe(ComparisonSourceType.Test);
}
Expand Down Expand Up @@ -349,14 +402,14 @@ public void Test2()
}

[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)
[InlineData(CompareDecision.Same | CompareDecision.SkipChildren)]
[InlineData(CompareDecision.Skip | CompareDecision.SkipChildren)]
public void Test3(CompareDecision decision)
{
var sut = CreateHtmlDiffer(
nodeMatcher: OneToOneNodeListMatcher,
nodeFilter: NoneNodeFilter,
nodeComparer: c => c.Control.Node.NodeName == "P" ? compareResult : throw new Exception("NODE COMPARER SHOULD NOT BE CALLED ON CHILD NODES"),
nodeComparer: c => c.Control.Node.NodeName == "P" ? new CompareResult(decision) : throw new Exception("NODE COMPARER SHOULD NOT BE CALLED ON CHILD NODES"),
attrMatcher: AttributeNameMatcher,
attrFilter: NoneAttrFilter,
attrComparer: SameResultAttrComparer
Expand All @@ -368,14 +421,14 @@ public void Test3(CompareResult compareResult)
}

[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)
[InlineData(CompareDecision.Same | CompareDecision.SkipAttributes)]
[InlineData(CompareDecision.Skip | CompareDecision.SkipAttributes)]
public void Test4(CompareDecision decision)
{
var sut = CreateHtmlDiffer(
nodeMatcher: OneToOneNodeListMatcher,
nodeFilter: NoneNodeFilter,
nodeComparer: c => compareResult,
nodeComparer: c => new CompareResult(decision),
attrMatcher: AttributeNameMatcher,
attrFilter: NoneAttrFilter,
attrComparer: SameResultAttrComparer
Expand All @@ -398,7 +451,7 @@ private static IEnumerable<Comparison> NoneNodeMatcher(IDiffContext ctx, SourceC
private static Func<IDiffContext, SourceCollection, SourceCollection, IEnumerable<Comparison>> SpecificIndexNodeMatcher(int index)
=> (ctx, controlNodes, testNodes) =>
{
return new List<Comparison> { new Comparison(controlNodes[index], testNodes[index]) };
return new List<Comparison> { new(controlNodes[index], testNodes[index]) };
};

private static IEnumerable<Comparison> OneToOneNodeListMatcher(
Expand All @@ -411,6 +464,7 @@ private static IEnumerable<Comparison> OneToOneNodeListMatcher(
#region NodeComparers
private static CompareResult SameResultNodeComparer(Comparison comparison) => CompareResult.Same;
private static CompareResult DiffResultNodeComparer(Comparison comparison) => CompareResult.Different;
private static CompareResult DiffResultCustomNodeComparer(Comparison comparison) => CompareResult.FromDiff(new CustomNodeDiff(comparison));
#endregion

#region AttributeMatchers
Expand All @@ -423,7 +477,7 @@ private static Func<IDiffContext, SourceMap, SourceMap, IEnumerable<AttributeCom
{
return (ctx, ctrlAttrs, testAttrs) => new List<AttributeComparison>
{
new AttributeComparison(ctrlAttrs[matchAttrName], testAttrs[matchAttrName] )
new(ctrlAttrs[matchAttrName], testAttrs[matchAttrName] )
};
}

Expand Down Expand Up @@ -452,5 +506,22 @@ private static Func<AttributeComparisonSource, FilterDecision> SpecificAttrFilte
#region AttributeComparers
public static CompareResult SameResultAttrComparer(AttributeComparison comparison) => CompareResult.Same;
public static CompareResult DiffResultAttrComparer(AttributeComparison comparison) => CompareResult.Different;
public static CompareResult DiffResultCustomAttrComparer(AttributeComparison comparison) => CompareResult.FromDiff(new CustomAttrDiff(comparison));
#endregion

#region CustomDiff
public sealed record CustomNodeDiff : NodeDiff
{
public CustomNodeDiff(in Comparison comparison) : base(comparison)
{
}
}

public sealed record CustomAttrDiff : AttrDiff
{
public CustomAttrDiff(in AttributeComparison comparison) : base(comparison, AttrDiffKind.Unspecified)
{
}
}
#endregion
}
4 changes: 2 additions & 2 deletions src/AngleSharp.Diffing.Tests/DiffBuilderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public void Test001()
[Fact(DisplayName = "Builder throws if null is passed to control and test")]
public void Test002()
{
Should.Throw<ArgumentNullException>(() => DiffBuilder.Compare(null!)).ParamName.ShouldBe(nameof(DiffBuilder.Control));
Should.Throw<ArgumentNullException>(() => DiffBuilder.Compare("").WithTest(null!)).ParamName.ShouldBe(nameof(DiffBuilder.Test));
Should.Throw<ArgumentNullException>(() => DiffBuilder.Compare(null!)).ParamName.ShouldBe("value");
Should.Throw<ArgumentNullException>(() => DiffBuilder.Compare("").WithTest(null!)).ParamName.ShouldBe("value");
}

[Fact(DisplayName = "Calling Build() with DefaultOptions() returns expected diffs")]
Expand Down
9 changes: 8 additions & 1 deletion src/AngleSharp.Diffing.Tests/DiffingTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@ public abstract class DiffingTestBase : IClassFixture<DiffingTestFixture>
{
private readonly DiffingTestFixture _testFixture;


public static readonly TheoryData<CompareResult> SameAndSkipCompareResult = new()
{
CompareResult.Same,
CompareResult.Skip,
};

protected IDiffContext DummyContext { get; } = new DiffContext(default(IElement), default(IElement));

protected INodeList EmptyNodeList => ToNodeList("");

public DiffingTestBase(DiffingTestFixture fixture)
protected DiffingTestBase(DiffingTestFixture fixture)
{
_testFixture = fixture;
}
Expand Down
9 changes: 9 additions & 0 deletions src/AngleSharp.Diffing.Tests/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.

using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Not relevant in tests.")]
[assembly: SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Not relevant in tests")]
Loading
Loading