From f3b8f590d3305da9ada1cee24772ef249eed3227 Mon Sep 17 00:00:00 2001 From: Tim Holzherr Date: Fri, 5 Aug 2022 13:54:20 +0200 Subject: [PATCH 1/9] Implement assertion with lambda expressions --- README.md | 31 ++++- src/Snapshooter.Json/Snapshot.cs | 29 +++-- src/Snapshooter.NUnit/Snapshot.cs | 37 ++++-- src/Snapshooter.NUnit/SnapshotExtension.cs | 25 ++++ .../Helpers/LambdaPathTests.cs | 109 ++++++++++++++++++ .../SnapshotExtensionTests.cs | 49 ++++++++ ...shot_FieldWithRandomInput_IgnoreField.snap | 56 +++++++++ ...ShouldFluentAssertions_RemovesSubject.snap | 58 ++++++++++ ...napshotMatchAssertions_RemovesSubject.snap | 56 +++++++++ src/Snapshooter.Xunit/Snapshot.cs | 37 ++++-- src/Snapshooter.Xunit/SnapshotExtension.cs | 25 ++++ .../LambdaPathConversionException.cs | 35 ++++++ .../Extensions/FluentAssertionsExtension.cs | 19 +++ src/Snapshooter/Helpers/LambdaPath.cs | 86 ++++++++++++++ src/Snapshooter/MatchOptions.cs | 2 +- src/Snapshooter/MatchOptionsOfModel.cs | 59 ++++++++++ src/Snapshooter/Snapshooter.csproj | 1 + ...ests.Match_WithLambdaExpression_Works.snap | 56 +++++++++ .../SnapshotExtensionTests.cs | 13 +++ test/Snapshooter.NUnit.Tests/SnapshotTests.cs | 12 +- ...ests.Match_WithLambdaExpression_Works.snap | 56 +++++++++ test/Snapshooter.Tests.Data/TestPerson.cs | 4 +- .../IsTypeField/IsTypeFieldTests.cs | 2 +- .../Snapshooter.Xunit.Tests.csproj | 1 + test/Snapshooter.Xunit.Tests/SnapshotTests.cs | 13 +++ 25 files changed, 833 insertions(+), 38 deletions(-) create mode 100644 src/Snapshooter.Tests/Helpers/LambdaPathTests.cs create mode 100644 src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs create mode 100644 src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_FieldWithRandomInput_IgnoreField.snap create mode 100644 src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertions_RemovesSubject.snap create mode 100644 src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_SnapshotMatchAssertions_RemovesSubject.snap create mode 100644 src/Snapshooter/Exceptions/LambdaPathConversionException.cs create mode 100644 src/Snapshooter/Extensions/FluentAssertionsExtension.cs create mode 100644 src/Snapshooter/Helpers/LambdaPath.cs create mode 100644 src/Snapshooter/MatchOptionsOfModel.cs create mode 100644 test/Snapshooter.MSTest.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap create mode 100644 test/Snapshooter.NUnit.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap diff --git a/README.md b/README.md index 4fa52af..1bfe87b 100644 --- a/README.md +++ b/README.md @@ -207,7 +207,36 @@ Snapshot.Match(person, matchOptions => matchOptions.HashField("Thumbnail Snapshot.Match(person, matchOptions => matchOptions.HashField("**.Data")); ``` -### Assert Fields in Snapshots +#### Ignore using a lambda expression + +We also support ignoring fields using lambda expressions instead of json paths. + +```csharp +// Ignores the Id field of the person +Snapshot.Match(person, matchOptions => matchOptions.IgnoreField(person => person.Id)); +``` + +### Assert the type of a field but ignore its value + +Using the `.AcceptField()` and `.AcceptFields()` syntax we can assert that a certain field contains a value of a given type. +Unlike ignore the snapshot file will not contain the field value but instead the type assertion, for example: `"Field": "AcceptAny"`. +This is desirable because the snapshot will not change when it is recreated at a later time. + +#### Example with json path syntax + +```csharp +// Assert that the id field is a guid and writes "AcceptAny" into the snapshot file +Snapshot.Match(person, matchOptions => matchOptions.AcceptField("id")); +``` + +#### Examle with lambda expressions + +```csharp +// Assert that the id field is a guid and writes "AcceptAny" into the snapshot file +Snapshot.Match(person, matchOptions => matchOptions.AcceptField(person => person.Id)); +``` + +### Assert Fields in Snapshots Matches Sometimes there are fields in a snapshot, which you want to assert separately against another value. diff --git a/src/Snapshooter.Json/Snapshot.cs b/src/Snapshooter.Json/Snapshot.cs index 42f1eb1..c7ebaec 100644 --- a/src/Snapshooter.Json/Snapshot.cs +++ b/src/Snapshooter.Json/Snapshot.cs @@ -1,4 +1,4 @@ -using System; +using System; using Snapshooter.Core; using Snapshooter.Core.Serialization; @@ -26,11 +26,15 @@ public static class Snapshot /// /// Additional compare actions, which can be applied during the comparison /// - public static void Match(T currentResult, - string snapshotName, - Func matchOptions = null) + public static void Match( + T currentResult, + string snapshotName, + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, snapshotName, matchOptions); + Match( + (object)currentResult, + snapshotName, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// @@ -56,12 +60,17 @@ public static void Match(T currentResult, /// /// Additional compare actions, which can be applied during the comparison /// - public static void Match(T currentResult, - string snapshotName, - SnapshotNameExtension snapshotNameExtension, - Func matchOptions = null) + public static void Match( + T currentResult, + string snapshotName, + SnapshotNameExtension snapshotNameExtension, + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, snapshotName, snapshotNameExtension, matchOptions); + Match( + (object)currentResult, + snapshotName, + snapshotNameExtension, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// diff --git a/src/Snapshooter.NUnit/Snapshot.cs b/src/Snapshooter.NUnit/Snapshot.cs index bd38485..8c562cf 100644 --- a/src/Snapshooter.NUnit/Snapshot.cs +++ b/src/Snapshooter.NUnit/Snapshot.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using Snapshooter.Core; using Snapshooter.Core.Serialization; @@ -30,9 +30,11 @@ public static class Snapshot /// public static void Match( T currentResult, - Func matchOptions = null) + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, matchOptions); + Match( + (object)currentResult, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// @@ -58,9 +60,12 @@ public static void Match( public static void Match( T currentResult, SnapshotNameExtension snapshotNameExtension, - Func matchOptions = null) + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, snapshotNameExtension, matchOptions); + Match( + (object)currentResult, + snapshotNameExtension, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// @@ -81,9 +86,12 @@ public static void Match( public static void Match( T currentResult, string snapshotName, - Func matchOptions = null) + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, snapshotName, matchOptions); + Match( + (object)currentResult, + snapshotName, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// @@ -114,9 +122,13 @@ public static void Match( T currentResult, string snapshotName, SnapshotNameExtension snapshotNameExtension, - Func matchOptions = null) + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, snapshotName, snapshotNameExtension, matchOptions); + Match( + (object)currentResult, + snapshotName, + snapshotNameExtension, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// @@ -134,9 +146,12 @@ public static void Match( public static void Match( T currentResult, SnapshotFullName snapshotFullName, - Func matchOptions = null) + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, snapshotFullName, matchOptions); + Match( + (object)currentResult, + snapshotFullName, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// diff --git a/src/Snapshooter.NUnit/SnapshotExtension.cs b/src/Snapshooter.NUnit/SnapshotExtension.cs index b57d5f8..f56b25d 100644 --- a/src/Snapshooter.NUnit/SnapshotExtension.cs +++ b/src/Snapshooter.NUnit/SnapshotExtension.cs @@ -1,4 +1,5 @@ using System; +using FluentAssertions; namespace Snapshooter.NUnit { @@ -127,5 +128,29 @@ public static void MatchSnapshot( var cleanedObject = currentResult.RemoveUnwantedWrappers(); Snapshot.Match(cleanedObject, snapshotFullName, matchOptions); } + + /// + /// Creates a json snapshot of the given object and compares it with the + /// already existing snapshot of the test. + /// If no snapshot exists, a new snapshot will be created from the current result + /// and saved under a certain file path, which will shown within the test message. + /// + /// The object to match. + /// + /// Additional compare actions, which can be applied during the snapshot comparison + /// + public static void MatchSnapshot( + this TypedAssertions currentResult, + Func, MatchOptions>? matchOptions = null) + { + var cleanedObject = currentResult.RemoveUnwantedWrappers(); + + Func? chainedMatchOptions = + matchOptions != null ? m => matchOptions(new MatchOptions(m)) : null; + + Snapshot.Match( + cleanedObject, + chainedMatchOptions); + } } } diff --git a/src/Snapshooter.Tests/Helpers/LambdaPathTests.cs b/src/Snapshooter.Tests/Helpers/LambdaPathTests.cs new file mode 100644 index 0000000..1712406 --- /dev/null +++ b/src/Snapshooter.Tests/Helpers/LambdaPathTests.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Snapshooter.Helpers; +using Xunit; +using System.Linq; +using FluentAssertions; +using Snapshooter.Exceptions; +using TupleList = System.Collections.Generic.List>>>; + + +namespace Snapshooter.Tests.Helpers +{ + public class LambdaPathTests + { + public static IEnumerable Data() => + new TupleList + { + new( + "FooBar", + m => m.FooBar), + new( + "Foo.Id", + m => m.Foo.Id), + new( + "NestedType.MoreNesting.EvenMore.Field", + m => m.NestedType.MoreNesting.EvenMore.Field), + new( + "Bars[0].Name", + m => m.Bars[0].Name), + new( + "NestedType.MoreNestingList[0].EvenMoreArray[0].Field", + m => m.NestedType.MoreNestingList[0].EvenMoreArray[0].Field), + new( + "Bars[*].Name", + m => m.Bars['*'].Name), + new( + "NestedType.MoreNestingList[*].EvenMoreArray[*].Field", + m => m.NestedType.MoreNestingList[-1].EvenMoreArray['*'].Field), + new( + "WithoutGetSet.MoreNesting.EvenMore.Field", + m => m.WithoutGetSet.MoreNesting.EvenMore.Field), + }.Select(t => new object[] { t.Item2, t.Item1 }); + + + [Theory] + [MemberData(nameof(Data))] + public void LambdaPath_ValidExpression_CorrectJsonPath( + Expression> expression, + string expected) + { + Assert.Equal(expected, LambdaPath.Get(expression)); + } + + [Fact] + public void LambdaPath_InvalidExpression_Exception() + { + // Act + Action action = () => LambdaPath.Get(m => m.IAmAFunction()); + + // Assert + action.Should().Throw(); + } + } + + public class Model + { + public Foo Foo { get; set; } + public IList Bars { get; set; } + + public string FooBar { get; set; } + + public NestedType NestedType { get; set; } + + public NestedType WithoutGetSet; + + public bool IAmAFunction() + { + return false; + } + } + + public class NestedType + { + public MoreNesting MoreNesting { get; set; } + public List MoreNestingList { get; set; } + } + + public class MoreNesting + { + public EvenMore EvenMore { get; set; } + public EvenMore[] EvenMoreArray { get; set; } + } + + public class EvenMore + { + public Guid Field { get; set; } + } + + public class Bar + { + public string Name { get; set; } + } + + public class Foo + { + public int Id { get; set; } + } +} diff --git a/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs b/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs new file mode 100644 index 0000000..fed2f05 --- /dev/null +++ b/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs @@ -0,0 +1,49 @@ +using System; +using FluentAssertions; +using Snapshooter.Tests.Data; +using Xunit; + +namespace Snapshooter.Xunit.Tests.LambdaExpressionsTests +{ + public class SnapshotExtensionTests + { + [Fact] + public void MatchSnapshot_ShouldFluentAssertions_RemovesSubject() + { + // arrange + TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton().Build(); + + // act & assert + testPerson.Should().MatchSnapshot(o => o + .AcceptField(m => m.Firstname) + .AcceptField(m => m.DateOfBirth) + .IgnoreFields(m => m.Children['*'].Name)); + } + + [Fact] + public void MatchSnapshot_SnapshotMatchAssertions_RemovesSubject() + { + // arrange + TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton().Build(); + + // act & assert + Snapshot.Match(testPerson, options => options + .AcceptField(m => m.Firstname) + .AcceptField(m => m.DateOfBirth) + .IgnoreFields(m => m.Children['*'].Name) + ); + } + + [Fact] + public void MatchSnapshot_FieldWithRandomInput_IgnoreField() + { + // arrange + TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton().Build(); + testPerson.Age = new Random().Next(0, 100); + + // act & assert + Snapshot.Match(testPerson, options => options + .IgnoreField(m => m.Age)); + } + } +} diff --git a/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_FieldWithRandomInput_IgnoreField.snap b/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_FieldWithRandomInput_IgnoreField.snap new file mode 100644 index 0000000..7f1688d --- /dev/null +++ b/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_FieldWithRandomInput_IgnoreField.snap @@ -0,0 +1,56 @@ +{ + "Id": "c78c698f-9ee5-4b4b-9a0e-ef729b1f8ec8", + "Firstname": "Mark", + "Lastname": "Walton", + "CreationDate": "2018-06-06T00:00:00", + "DateOfBirth": "2000-06-25T00:00:00", + "Age": 84, + "Size": 182.5214, + "Address": { + "Street": "Rohrstrasse", + "StreetNumber": 12, + "Plz": 8304, + "City": "Wallislellen", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [ + { + "Name": "James", + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": null, + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": "Hanna", + "DateOfBirth": "2012-03-20T00:00:00" + } + ], + "Relatives": [ + { + "Id": "fcf04ca6-d8f2-4214-a3ff-d0ded5bad4de", + "Firstname": "Sandra", + "Lastname": "Schneider", + "CreationDate": "2019-04-01T00:00:00", + "DateOfBirth": "1996-02-14T00:00:00", + "Age": null, + "Size": 165.23, + "Address": { + "Street": "Bahnhofstrasse", + "StreetNumber": 450, + "Plz": 8000, + "City": "Zurich", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [], + "Relatives": null + } + ] +} diff --git a/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertions_RemovesSubject.snap b/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertions_RemovesSubject.snap new file mode 100644 index 0000000..e3910e2 --- /dev/null +++ b/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertions_RemovesSubject.snap @@ -0,0 +1,58 @@ + + +{ + "Id": "c78c698f-9ee5-4b4b-9a0e-ef729b1f8ec8", + "Firstname": "AcceptAny", + "Lastname": "Walton", + "CreationDate": "2018-06-06T00:00:00", + "DateOfBirth": "AcceptAny", + "Age": 30, + "Size": 182.5214, + "Address": { + "Street": "Rohrstrasse", + "StreetNumber": 12, + "Plz": 8304, + "City": "Wallislellen", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [ + { + "Name": "James123", + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": null, + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": "Hanna", + "DateOfBirth": "2012-03-20T00:00:00" + } + ], + "Relatives": [ + { + "Id": "fcf04ca6-d8f2-4214-a3ff-d0ded5bad4de", + "Firstname": "Sandra", + "Lastname": "Schneider", + "CreationDate": "2019-04-01T00:00:00", + "DateOfBirth": "1996-02-14T00:00:00", + "Age": null, + "Size": 165.23, + "Address": { + "Street": "Bahnhofstrasse", + "StreetNumber": 450, + "Plz": 8000, + "City": "Zurich", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [], + "Relatives": null + } + ] +} diff --git a/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_SnapshotMatchAssertions_RemovesSubject.snap b/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_SnapshotMatchAssertions_RemovesSubject.snap new file mode 100644 index 0000000..cc7b7b3 --- /dev/null +++ b/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_SnapshotMatchAssertions_RemovesSubject.snap @@ -0,0 +1,56 @@ +{ + "Id": "c78c698f-9ee5-4b4b-9a0e-ef729b1f8ec8", + "Firstname": "AcceptAny", + "Lastname": "Walton", + "CreationDate": "2018-06-06T00:00:00", + "DateOfBirth": "AcceptAny", + "Age": 30, + "Size": 182.5214, + "Address": { + "Street": "Rohrstrasse", + "StreetNumber": 12, + "Plz": 8304, + "City": "Wallislellen", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [ + { + "Name": "James", + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": null, + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": "Hanna", + "DateOfBirth": "2012-03-20T00:00:00" + } + ], + "Relatives": [ + { + "Id": "fcf04ca6-d8f2-4214-a3ff-d0ded5bad4de", + "Firstname": "Sandra", + "Lastname": "Schneider", + "CreationDate": "2019-04-01T00:00:00", + "DateOfBirth": "1996-02-14T00:00:00", + "Age": null, + "Size": 165.23, + "Address": { + "Street": "Bahnhofstrasse", + "StreetNumber": 450, + "Plz": 8000, + "City": "Zurich", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [], + "Relatives": null + } + ] +} diff --git a/src/Snapshooter.Xunit/Snapshot.cs b/src/Snapshooter.Xunit/Snapshot.cs index 2d62eb1..41d44fc 100644 --- a/src/Snapshooter.Xunit/Snapshot.cs +++ b/src/Snapshooter.Xunit/Snapshot.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using Snapshooter.Core; using Snapshooter.Core.Serialization; @@ -30,9 +30,11 @@ public static class Snapshot /// public static void Match( T currentResult, - Func matchOptions = null) + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, matchOptions); + Match( + (object)currentResult, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// @@ -58,9 +60,12 @@ public static void Match( public static void Match( T currentResult, SnapshotNameExtension snapshotNameExtension, - Func matchOptions = null) + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, snapshotNameExtension, matchOptions); + Match( + (object)currentResult, + snapshotNameExtension, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// @@ -81,9 +86,12 @@ public static void Match( public static void Match( T currentResult, string snapshotName, - Func matchOptions = null) + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, snapshotName, matchOptions); + Match( + (object)currentResult, + snapshotName, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// @@ -114,9 +122,13 @@ public static void Match( T currentResult, string snapshotName, SnapshotNameExtension snapshotNameExtension, - Func matchOptions = null) + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, snapshotName, snapshotNameExtension, matchOptions); + Match( + (object)currentResult, + snapshotName, + snapshotNameExtension, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// @@ -134,9 +146,12 @@ public static void Match( public static void Match( T currentResult, SnapshotFullName snapshotFullName, - Func matchOptions = null) + Func, MatchOptions> matchOptions = null) { - Match((object)currentResult, snapshotFullName, matchOptions); + Match( + (object)currentResult, + snapshotFullName, + o => matchOptions == null ? o : matchOptions(new MatchOptions(o))); } /// diff --git a/src/Snapshooter.Xunit/SnapshotExtension.cs b/src/Snapshooter.Xunit/SnapshotExtension.cs index afdff1c..308e225 100644 --- a/src/Snapshooter.Xunit/SnapshotExtension.cs +++ b/src/Snapshooter.Xunit/SnapshotExtension.cs @@ -1,4 +1,5 @@ using System; +using FluentAssertions; namespace Snapshooter.Xunit { @@ -127,5 +128,29 @@ public static void MatchSnapshot( var cleanedObject = currentResult.RemoveUnwantedWrappers(); Snapshot.Match(cleanedObject, snapshotFullName, matchOptions); } + + /// + /// Creates a json snapshot of the given object and compares it with the + /// already existing snapshot of the test. + /// If no snapshot exists, a new snapshot will be created from the current result + /// and saved under a certain file path, which will shown within the test message. + /// + /// The object to match. + /// + /// Additional compare actions, which can be applied during the snapshot comparison + /// + public static void MatchSnapshot( + this TypedAssertions currentResult, + Func, MatchOptions>? matchOptions = null) + { + var cleanedObject = currentResult.RemoveUnwantedWrappers(); + + Func? chainedMatchOptions = + matchOptions != null ? m => matchOptions(new MatchOptions(m)) : null; + + Snapshot.Match( + cleanedObject, + chainedMatchOptions); + } } } diff --git a/src/Snapshooter/Exceptions/LambdaPathConversionException.cs b/src/Snapshooter/Exceptions/LambdaPathConversionException.cs new file mode 100644 index 0000000..3045417 --- /dev/null +++ b/src/Snapshooter/Exceptions/LambdaPathConversionException.cs @@ -0,0 +1,35 @@ +using System; + +namespace Snapshooter.Exceptions; + +/// +/// Exception thrown if something went wrong during converting a lambda expression into a path. +/// +public class LambdaPathConversionException : SnapshotTestException +{ + /// + /// Initializes the + /// + public LambdaPathConversionException() + { + } + + /// + /// Initializes the + /// The exception message. + /// + public LambdaPathConversionException(string message) + : base(message) + { + } + + /// + /// Initializes the + /// The exception message. + /// The inner exception. + /// + public LambdaPathConversionException(string message, Exception inner) + : base(message, inner) + { + } +} diff --git a/src/Snapshooter/Extensions/FluentAssertionsExtension.cs b/src/Snapshooter/Extensions/FluentAssertionsExtension.cs new file mode 100644 index 0000000..21d99e8 --- /dev/null +++ b/src/Snapshooter/Extensions/FluentAssertionsExtension.cs @@ -0,0 +1,19 @@ +using FluentAssertions.Primitives; + +// Extension of Fluent assertion to not loose type information of asserted object +namespace FluentAssertions; + +public class TypedAssertions : ObjectAssertions> +{ + public TypedAssertions(T value) : base(value) + { + } +} + +public static class Extensions +{ + public static TypedAssertions Should(this TSubject actualValue) + { + return new TypedAssertions(actualValue); + } +} diff --git a/src/Snapshooter/Helpers/LambdaPath.cs b/src/Snapshooter/Helpers/LambdaPath.cs new file mode 100644 index 0000000..1f6df95 --- /dev/null +++ b/src/Snapshooter/Helpers/LambdaPath.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Snapshooter.Exceptions; + +namespace Snapshooter.Helpers; + +/// +/// Converts a Lambda Expression into a JsonPath +/// +public static class LambdaPath +{ + /// + /// Converts the given expression into a jsonPath + /// + public static string Get(Expression> expression) + { + var visitor = new PathPrintVisitor(); + visitor.Visit(expression.Body); + visitor.Path.Reverse(); + + var joinedPath = string.Join("", visitor.Path); + + if (string.IsNullOrWhiteSpace(joinedPath)) + { + throw new LambdaPathConversionException( + "Given lambda expression could not be converted to path"); + } + + return joinedPath.StartsWith(".") ? joinedPath.Substring(1) : joinedPath; + } + + private class PathPrintVisitor : ExpressionVisitor + { + internal readonly List Path = new(); + + protected override Expression VisitMember(MemberExpression node) + { + if (!(node.Member is PropertyInfo or FieldInfo)) + { + throw new LambdaPathConversionException( + "The path can only contain properties or fields"); + } + + Path.Add($".{node.Member.Name}"); + return base.VisitMember(node); + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + if (node.Method.Name == "get_Item") // index access for IList + { + Expression argument = node.Arguments.FirstOrDefault(); + if (argument != null) + { + HandleIndexes(argument); + } + } + + return base.VisitMethodCall(node); + } + + private void HandleIndexes(Expression argument) + { + if (argument.NodeType == ExpressionType.Constant && argument.Type == typeof(int)) + { + int? value = (int?)((ConstantExpression)argument).Value; + // the char '*' will be cast to the integer 42 + Path.Add(value is 42 or -1 or null ? "[*]" : $"[{value}]"); + } + } + + protected override Expression VisitBinary(BinaryExpression node) + { + if (node.NodeType == ExpressionType.ArrayIndex && + node.Right.NodeType == ExpressionType.Constant) + { + HandleIndexes(node.Right); + } + + return base.VisitBinary(node); + } + } +} diff --git a/src/Snapshooter/MatchOptions.cs b/src/Snapshooter/MatchOptions.cs index 1010739..f1ab06d 100644 --- a/src/Snapshooter/MatchOptions.cs +++ b/src/Snapshooter/MatchOptions.cs @@ -508,7 +508,7 @@ private MatchOptions AddIgnoreMatchOperator(string fieldsPath) return this; } - private MatchOptions Accept( + protected MatchOptions Accept( string fieldsPath, bool keepOriginalValue = false) { diff --git a/src/Snapshooter/MatchOptionsOfModel.cs b/src/Snapshooter/MatchOptionsOfModel.cs new file mode 100644 index 0000000..60e889f --- /dev/null +++ b/src/Snapshooter/MatchOptionsOfModel.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using Snapshooter.Helpers; + +namespace Snapshooter; + +/// +/// which also knows the type of the object we assert against. +/// +public class MatchOptions : MatchOptions +{ + /// + /// Constructor of the class to create + /// a new instance. + /// + public MatchOptions(MatchOptions predecessor) + { + _matchOperators = predecessor.MatchOperators.ToList(); + } + + /// + /// The match option accepts a + /// lambda expression pointing to a field. The value of the field will NOT be + /// compared with the original snapshot. + /// In addition, the field will be replaced by the text 'AcceptAny{T}()'" + /// + public MatchOptions AcceptField(Expression> field) + { + var path = LambdaPath.Get(field); + Accept(path); + + return this; + } + + /// + /// The option ignores the existing field given by the + /// lambda expression. The field will be ignored during snapshot comparison. + /// + public MatchOptions IgnoreField(Expression> field) + { + var path = LambdaPath.Get(field); + IgnoreField(path); + + return this; + } + + /// + /// The option ignores the existing field(s) given by the + /// lambda expression. The field(s) will be ignored during snapshot comparison. + /// + public MatchOptions IgnoreFields(Expression> fields) + { + var path = LambdaPath.Get(fields); + IgnoreFields(path); + + return this; + } +} diff --git a/src/Snapshooter/Snapshooter.csproj b/src/Snapshooter/Snapshooter.csproj index a6b1936..3ca191b 100644 --- a/src/Snapshooter/Snapshooter.csproj +++ b/src/Snapshooter/Snapshooter.csproj @@ -14,6 +14,7 @@ + diff --git a/test/Snapshooter.MSTest.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap b/test/Snapshooter.MSTest.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap new file mode 100644 index 0000000..ccb3e6d --- /dev/null +++ b/test/Snapshooter.MSTest.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap @@ -0,0 +1,56 @@ +{ + "Id": "c78c698f-9ee5-4b4b-9a0e-ef729b1f8ec8", + "Firstname": "Mark", + "Lastname": "Walton", + "CreationDate": "2018-06-06T00:00:00", + "DateOfBirth": "2000-06-25T00:00:00", + "Age": "AcceptAny", + "Size": 182.5214, + "Address": { + "Street": "Rohrstrasse", + "StreetNumber": 12, + "Plz": 8304, + "City": "Wallislellen", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [ + { + "Name": "James", + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": null, + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": "Hanna", + "DateOfBirth": "2012-03-20T00:00:00" + } + ], + "Relatives": [ + { + "Id": "fcf04ca6-d8f2-4214-a3ff-d0ded5bad4de", + "Firstname": "Sandra", + "Lastname": "Schneider", + "CreationDate": "2019-04-01T00:00:00", + "DateOfBirth": "1996-02-14T00:00:00", + "Age": null, + "Size": 165.23, + "Address": { + "Street": "Bahnhofstrasse", + "StreetNumber": 450, + "Plz": 8000, + "City": "Zurich", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [], + "Relatives": null + } + ] +} diff --git a/test/Snapshooter.NUnit.Tests/SnapshotExtensionTests.cs b/test/Snapshooter.NUnit.Tests/SnapshotExtensionTests.cs index a2edd5f..2277afa 100644 --- a/test/Snapshooter.NUnit.Tests/SnapshotExtensionTests.cs +++ b/test/Snapshooter.NUnit.Tests/SnapshotExtensionTests.cs @@ -97,5 +97,18 @@ public void MatchSnapshot_Null_Throws() // act & assert Assert.Throws(() => testPerson.MatchSnapshot()); } + + [Test] + public void MatchSnapshot_ShouldFluentAssertionsWithLambda_RemovesSubject() + { + // arrange + TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton().Build(); + + // act & assert + testPerson.Should().MatchSnapshot(o => o + .AcceptField(m => m.Firstname) + .AcceptField(m => m.DateOfBirth) + .IgnoreFields(m => m.Children['*'].Name)); + } } } diff --git a/test/Snapshooter.NUnit.Tests/SnapshotTests.cs b/test/Snapshooter.NUnit.Tests/SnapshotTests.cs index 5b0feb0..7b40c9b 100644 --- a/test/Snapshooter.NUnit.Tests/SnapshotTests.cs +++ b/test/Snapshooter.NUnit.Tests/SnapshotTests.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using NUnit.Framework; using Snapshooter.Tests.Data; @@ -132,6 +132,16 @@ public void Match_TestCaseMatchNewSingleSnapshot_ExpectedSnapshotHasBeenCreated( Assert.True(File.Exists(snapshotFileName)); } + [Test] + public void Match_WithLambdaExpression_Works() + { + // arrange + TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton().Build(); + + // act & assert + Snapshot.Match(testPerson, o => o.AcceptField(t => t.Age)); + } + #endregion } } diff --git a/test/Snapshooter.NUnit.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap b/test/Snapshooter.NUnit.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap new file mode 100644 index 0000000..b115d4f --- /dev/null +++ b/test/Snapshooter.NUnit.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap @@ -0,0 +1,56 @@ +{ + "Id": "c78c698f-9ee5-4b4b-9a0e-ef729b1f8ec8", + "Firstname": "Mark", + "Lastname": "Walton", + "CreationDate": "2018-06-06T00:00:00", + "DateOfBirth": "2000-06-25T00:00:00", + "Age": 30, + "Size": 182.5214, + "Address": { + "Street": "Rohrstrasse", + "StreetNumber": 12, + "Plz": 8304, + "City": "Wallislellen", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [ + { + "Name": "James", + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": null, + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": "Hanna", + "DateOfBirth": "2012-03-20T00:00:00" + } + ], + "Relatives": [ + { + "Id": "fcf04ca6-d8f2-4214-a3ff-d0ded5bad4de", + "Firstname": "Sandra", + "Lastname": "Schneider", + "CreationDate": "2019-04-01T00:00:00", + "DateOfBirth": "1996-02-14T00:00:00", + "Age": null, + "Size": 165.23, + "Address": { + "Street": "Bahnhofstrasse", + "StreetNumber": 450, + "Plz": 8000, + "City": "Zurich", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [], + "Relatives": null + } + ] +} diff --git a/test/Snapshooter.Tests.Data/TestPerson.cs b/test/Snapshooter.Tests.Data/TestPerson.cs index bfcecc5..ff31e96 100644 --- a/test/Snapshooter.Tests.Data/TestPerson.cs +++ b/test/Snapshooter.Tests.Data/TestPerson.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; @@ -19,7 +19,7 @@ public TestPerson() public int? Age { get; set; } public decimal? Size { get; set; } public TestAddress Address { get; set; } - public IEnumerable Children { get; set; } + public IList Children { get; set; } public TestPerson[] Relatives { get; set; } } diff --git a/test/Snapshooter.Xunit.Tests/MatchOptions/IsTypeField/IsTypeFieldTests.cs b/test/Snapshooter.Xunit.Tests/MatchOptions/IsTypeField/IsTypeFieldTests.cs index 52c8c51..2633a7c 100644 --- a/test/Snapshooter.Xunit.Tests/MatchOptions/IsTypeField/IsTypeFieldTests.cs +++ b/test/Snapshooter.Xunit.Tests/MatchOptions/IsTypeField/IsTypeFieldTests.cs @@ -225,7 +225,7 @@ public void Match_IsTypeWildcardScalarFieldsArrayDifferentTypes_Error() }; // act & assert - Assert.Throws(() => Snapshot.Match(theThing, + Assert.Throws(() => Snapshot.Match(theThing, matchOptions => matchOptions.IsTypeFields("**.Value"))); } diff --git a/test/Snapshooter.Xunit.Tests/Snapshooter.Xunit.Tests.csproj b/test/Snapshooter.Xunit.Tests/Snapshooter.Xunit.Tests.csproj index 8c825d3..cb6feff 100644 --- a/test/Snapshooter.Xunit.Tests/Snapshooter.Xunit.Tests.csproj +++ b/test/Snapshooter.Xunit.Tests/Snapshooter.Xunit.Tests.csproj @@ -41,6 +41,7 @@ + diff --git a/test/Snapshooter.Xunit.Tests/SnapshotTests.cs b/test/Snapshooter.Xunit.Tests/SnapshotTests.cs index 8180579..e5918a4 100644 --- a/test/Snapshooter.Xunit.Tests/SnapshotTests.cs +++ b/test/Snapshooter.Xunit.Tests/SnapshotTests.cs @@ -152,6 +152,19 @@ public void Match_TheoryMatchNewSingleSnapshot_ExpectedSnapshotHasBeenCreated(in Assert.True(File.Exists(snapshotFileName)); } + [Fact] + public void Match_WithLambdaExpression_Works() + { + // arrange + string snapshotName = nameof(SnapshotTests) + "." + + nameof(Match_FactMatchSingleSnapshot_GoodCase); + + TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton().Build(); + + // act & assert + Snapshot.Match(testPerson, snapshotName, o => o.AcceptField(p => p.Age)); + } + #endregion #region Match Snapshot - Multiple Objects Tests From de92d2fd94c6461145b74d69ea82852cbf2396fa Mon Sep 17 00:00:00 2001 From: Tim Holzherr Date: Fri, 3 Mar 2023 11:47:31 +0100 Subject: [PATCH 2/9] Fix ObjectWrapperRemover --- src/Snapshooter/Helpers/ObjectWrapperRemover.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Snapshooter/Helpers/ObjectWrapperRemover.cs b/src/Snapshooter/Helpers/ObjectWrapperRemover.cs index 1f57450..cd18ba5 100644 --- a/src/Snapshooter/Helpers/ObjectWrapperRemover.cs +++ b/src/Snapshooter/Helpers/ObjectWrapperRemover.cs @@ -19,7 +19,7 @@ private static object RemoveFluentAssertionsWrapper(this object objectToRemoveWr { Type resultType = objectToRemoveWrappers.GetType(); - if (resultType.Namespace == null || !resultType.Namespace.StartsWith("FluentAssertions.")) + if (resultType.Namespace == null || !resultType.Namespace.StartsWith("FluentAssertions")) { return objectToRemoveWrappers; } From 5406eae1cf1aafcc13818d1f342fbe869d584395 Mon Sep 17 00:00:00 2001 From: Tim Holzherr Date: Fri, 3 Mar 2023 13:16:29 +0100 Subject: [PATCH 3/9] remove warnings about missing editor hints in FluentAssertionsExtension.cs --- .../Extensions/FluentAssertionsExtension.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Snapshooter/Extensions/FluentAssertionsExtension.cs b/src/Snapshooter/Extensions/FluentAssertionsExtension.cs index 21d99e8..31bddef 100644 --- a/src/Snapshooter/Extensions/FluentAssertionsExtension.cs +++ b/src/Snapshooter/Extensions/FluentAssertionsExtension.cs @@ -3,15 +3,25 @@ // Extension of Fluent assertion to not loose type information of asserted object namespace FluentAssertions; +/// +/// Contains a number of methods to assert that an is in the expected state. +/// public class TypedAssertions : ObjectAssertions> { - public TypedAssertions(T value) : base(value) + internal TypedAssertions(T value) : base(value) { } } -public static class Extensions +/// +/// Contains extension methods for custom assertions in unit tests. +/// +public static class AssertionExtensions { + /// + /// Returns an object that can be used to assert the + /// current . + /// public static TypedAssertions Should(this TSubject actualValue) { return new TypedAssertions(actualValue); From a7bf01ede0197d05ed18af2b2866def2b6bb7ed7 Mon Sep 17 00:00:00 2001 From: Tim Holzherr Date: Fri, 17 Mar 2023 15:20:03 +0100 Subject: [PATCH 4/9] fix merge issues --- src/Snapshooter/MatchOptions.cs | 5 ++ src/Snapshooter/MatchOptionsOfModel.cs | 2 +- test/Snapshooter.NUnit.Tests/SnapshotTests.cs | 6 +- ...ntAssertionsWithLambda_RemovesSubject.snap | 0 ...ests.Match_WithLambdaExpression_Works.snap | 2 +- .../Helpers/LambdaPathTests.cs | 0 .../SnapshotExtensionTests.cs | 0 ...shot_FieldWithRandomInput_IgnoreField.snap | 0 ...ShouldFluentAssertions_RemovesSubject.snap | 6 +- ...napshotMatchAssertions_RemovesSubject.snap | 56 +++++++++++++++++++ 10 files changed, 70 insertions(+), 7 deletions(-) rename src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_SnapshotMatchAssertions_RemovesSubject.snap => test/Snapshooter.NUnit.Tests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertionsWithLambda_RemovesSubject.snap (100%) rename {src => test}/Snapshooter.Tests/Helpers/LambdaPathTests.cs (100%) rename {src => test}/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs (100%) rename {src => test}/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_FieldWithRandomInput_IgnoreField.snap (100%) rename {src => test}/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertions_RemovesSubject.snap (97%) create mode 100644 test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_SnapshotMatchAssertions_RemovesSubject.snap diff --git a/src/Snapshooter/MatchOptions.cs b/src/Snapshooter/MatchOptions.cs index f1ab06d..ff75a85 100644 --- a/src/Snapshooter/MatchOptions.cs +++ b/src/Snapshooter/MatchOptions.cs @@ -35,6 +35,11 @@ public IReadOnlyCollection MatchOperators get { return _matchOperators.AsReadOnly(); } } + internal List MatchOperatorsInternal + { + get { return _matchOperators; } + } + /// /// The match option accepts a snapshot field, if the /// type of the field is equal to the given Type. The value of the field will NOT be diff --git a/src/Snapshooter/MatchOptionsOfModel.cs b/src/Snapshooter/MatchOptionsOfModel.cs index 60e889f..01ee9c1 100644 --- a/src/Snapshooter/MatchOptionsOfModel.cs +++ b/src/Snapshooter/MatchOptionsOfModel.cs @@ -16,7 +16,7 @@ public class MatchOptions : MatchOptions /// public MatchOptions(MatchOptions predecessor) { - _matchOperators = predecessor.MatchOperators.ToList(); + _matchOperators = predecessor.MatchOperatorsInternal; } /// diff --git a/test/Snapshooter.NUnit.Tests/SnapshotTests.cs b/test/Snapshooter.NUnit.Tests/SnapshotTests.cs index 7b40c9b..d1c8b0a 100644 --- a/test/Snapshooter.NUnit.Tests/SnapshotTests.cs +++ b/test/Snapshooter.NUnit.Tests/SnapshotTests.cs @@ -1,5 +1,8 @@ +using System; using System.IO; +using System.Linq; using NUnit.Framework; +using Snapshooter.Exceptions; using Snapshooter.Tests.Data; namespace Snapshooter.NUnit.Tests @@ -136,7 +139,8 @@ public void Match_TestCaseMatchNewSingleSnapshot_ExpectedSnapshotHasBeenCreated( public void Match_WithLambdaExpression_Works() { // arrange - TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton().Build(); + TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton() + .WithAge(new Random().Next()).Build(); // act & assert Snapshot.Match(testPerson, o => o.AcceptField(t => t.Age)); diff --git a/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_SnapshotMatchAssertions_RemovesSubject.snap b/test/Snapshooter.NUnit.Tests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertionsWithLambda_RemovesSubject.snap similarity index 100% rename from src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_SnapshotMatchAssertions_RemovesSubject.snap rename to test/Snapshooter.NUnit.Tests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertionsWithLambda_RemovesSubject.snap diff --git a/test/Snapshooter.NUnit.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap b/test/Snapshooter.NUnit.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap index b115d4f..ccb3e6d 100644 --- a/test/Snapshooter.NUnit.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap +++ b/test/Snapshooter.NUnit.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap @@ -4,7 +4,7 @@ "Lastname": "Walton", "CreationDate": "2018-06-06T00:00:00", "DateOfBirth": "2000-06-25T00:00:00", - "Age": 30, + "Age": "AcceptAny", "Size": 182.5214, "Address": { "Street": "Rohrstrasse", diff --git a/src/Snapshooter.Tests/Helpers/LambdaPathTests.cs b/test/Snapshooter.Tests/Helpers/LambdaPathTests.cs similarity index 100% rename from src/Snapshooter.Tests/Helpers/LambdaPathTests.cs rename to test/Snapshooter.Tests/Helpers/LambdaPathTests.cs diff --git a/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs b/test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs similarity index 100% rename from src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs rename to test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs diff --git a/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_FieldWithRandomInput_IgnoreField.snap b/test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_FieldWithRandomInput_IgnoreField.snap similarity index 100% rename from src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_FieldWithRandomInput_IgnoreField.snap rename to test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_FieldWithRandomInput_IgnoreField.snap diff --git a/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertions_RemovesSubject.snap b/test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertions_RemovesSubject.snap similarity index 97% rename from src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertions_RemovesSubject.snap rename to test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertions_RemovesSubject.snap index e3910e2..cc7b7b3 100644 --- a/src/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertions_RemovesSubject.snap +++ b/test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_ShouldFluentAssertions_RemovesSubject.snap @@ -1,6 +1,4 @@ - - -{ +{ "Id": "c78c698f-9ee5-4b4b-9a0e-ef729b1f8ec8", "Firstname": "AcceptAny", "Lastname": "Walton", @@ -20,7 +18,7 @@ }, "Children": [ { - "Name": "James123", + "Name": "James", "DateOfBirth": "2015-02-12T00:00:00" }, { diff --git a/test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_SnapshotMatchAssertions_RemovesSubject.snap b/test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_SnapshotMatchAssertions_RemovesSubject.snap new file mode 100644 index 0000000..cc7b7b3 --- /dev/null +++ b/test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/__snapshots__/SnapshotExtensionTests.MatchSnapshot_SnapshotMatchAssertions_RemovesSubject.snap @@ -0,0 +1,56 @@ +{ + "Id": "c78c698f-9ee5-4b4b-9a0e-ef729b1f8ec8", + "Firstname": "AcceptAny", + "Lastname": "Walton", + "CreationDate": "2018-06-06T00:00:00", + "DateOfBirth": "AcceptAny", + "Age": 30, + "Size": 182.5214, + "Address": { + "Street": "Rohrstrasse", + "StreetNumber": 12, + "Plz": 8304, + "City": "Wallislellen", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [ + { + "Name": "James", + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": null, + "DateOfBirth": "2015-02-12T00:00:00" + }, + { + "Name": "Hanna", + "DateOfBirth": "2012-03-20T00:00:00" + } + ], + "Relatives": [ + { + "Id": "fcf04ca6-d8f2-4214-a3ff-d0ded5bad4de", + "Firstname": "Sandra", + "Lastname": "Schneider", + "CreationDate": "2019-04-01T00:00:00", + "DateOfBirth": "1996-02-14T00:00:00", + "Age": null, + "Size": 165.23, + "Address": { + "Street": "Bahnhofstrasse", + "StreetNumber": 450, + "Plz": 8000, + "City": "Zurich", + "Country": { + "Name": "Switzerland", + "CountryCode": "CH" + } + }, + "Children": [], + "Relatives": null + } + ] +} From 0c94dc5d41cbaa986f9372baf3c97986296fe19b Mon Sep 17 00:00:00 2001 From: Tim Holzherr Date: Fri, 17 Mar 2023 15:29:37 +0100 Subject: [PATCH 5/9] remove no longer needed snapshot --- ...ests.Match_WithLambdaExpression_Works.snap | 56 ------------------- 1 file changed, 56 deletions(-) delete mode 100644 test/Snapshooter.MSTest.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap diff --git a/test/Snapshooter.MSTest.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap b/test/Snapshooter.MSTest.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap deleted file mode 100644 index ccb3e6d..0000000 --- a/test/Snapshooter.MSTest.Tests/__snapshots__/SnapshotTests.Match_WithLambdaExpression_Works.snap +++ /dev/null @@ -1,56 +0,0 @@ -{ - "Id": "c78c698f-9ee5-4b4b-9a0e-ef729b1f8ec8", - "Firstname": "Mark", - "Lastname": "Walton", - "CreationDate": "2018-06-06T00:00:00", - "DateOfBirth": "2000-06-25T00:00:00", - "Age": "AcceptAny", - "Size": 182.5214, - "Address": { - "Street": "Rohrstrasse", - "StreetNumber": 12, - "Plz": 8304, - "City": "Wallislellen", - "Country": { - "Name": "Switzerland", - "CountryCode": "CH" - } - }, - "Children": [ - { - "Name": "James", - "DateOfBirth": "2015-02-12T00:00:00" - }, - { - "Name": null, - "DateOfBirth": "2015-02-12T00:00:00" - }, - { - "Name": "Hanna", - "DateOfBirth": "2012-03-20T00:00:00" - } - ], - "Relatives": [ - { - "Id": "fcf04ca6-d8f2-4214-a3ff-d0ded5bad4de", - "Firstname": "Sandra", - "Lastname": "Schneider", - "CreationDate": "2019-04-01T00:00:00", - "DateOfBirth": "1996-02-14T00:00:00", - "Age": null, - "Size": 165.23, - "Address": { - "Street": "Bahnhofstrasse", - "StreetNumber": 450, - "Plz": 8000, - "City": "Zurich", - "Country": { - "Name": "Switzerland", - "CountryCode": "CH" - } - }, - "Children": [], - "Relatives": null - } - ] -} From 3bb301d909c1a4fc11a82d27da51ab989e86be69 Mon Sep 17 00:00:00 2001 From: Tim Holzherr Date: Fri, 17 Mar 2023 15:44:45 +0100 Subject: [PATCH 6/9] fix review comment --- .../SnapshotExtensionTests.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs b/test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs index fed2f05..c8fea01 100644 --- a/test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs +++ b/test/Snapshooter.Xunit.Tests/LambdaExpressionsTests/SnapshotExtensionTests.cs @@ -11,7 +11,7 @@ public class SnapshotExtensionTests public void MatchSnapshot_ShouldFluentAssertions_RemovesSubject() { // arrange - TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton().Build(); + TestPerson testPerson = CreateTestPersonWithRandomData(); // act & assert testPerson.Should().MatchSnapshot(o => o @@ -24,7 +24,7 @@ public void MatchSnapshot_ShouldFluentAssertions_RemovesSubject() public void MatchSnapshot_SnapshotMatchAssertions_RemovesSubject() { // arrange - TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton().Build(); + TestPerson testPerson = CreateTestPersonWithRandomData(); // act & assert Snapshot.Match(testPerson, options => options @@ -34,12 +34,27 @@ public void MatchSnapshot_SnapshotMatchAssertions_RemovesSubject() ); } + private static TestPerson CreateTestPersonWithRandomData() + { + TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton() + .WithFirstname(new Random().Next().ToString()) + .WithDateOfBirth(DateTime.Now) + .Build(); + + foreach (var testPersonChild in testPerson.Children) + { + testPersonChild.Name = new Random().Next().ToString(); + } + + return testPerson; + } + [Fact] public void MatchSnapshot_FieldWithRandomInput_IgnoreField() { // arrange - TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton().Build(); - testPerson.Age = new Random().Next(0, 100); + TestPerson testPerson = TestDataBuilder.TestPersonMarkWalton() + .WithAge(new Random().Next(0, 100)).Build(); // act & assert Snapshot.Match(testPerson, options => options From cf69456a518750b30848ce24f5703f68c0779a3a Mon Sep 17 00:00:00 2001 From: Tim Holzherr Date: Fri, 17 Mar 2023 15:51:48 +0100 Subject: [PATCH 7/9] fix naming --- test/Snapshooter.Xunit.Tests/SnapshotTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Snapshooter.Xunit.Tests/SnapshotTests.cs b/test/Snapshooter.Xunit.Tests/SnapshotTests.cs index e5918a4..b866e9e 100644 --- a/test/Snapshooter.Xunit.Tests/SnapshotTests.cs +++ b/test/Snapshooter.Xunit.Tests/SnapshotTests.cs @@ -153,7 +153,7 @@ public void Match_TheoryMatchNewSingleSnapshot_ExpectedSnapshotHasBeenCreated(in } [Fact] - public void Match_WithLambdaExpression_Works() + public void Match_WithLambdaExpressionAndProvidedSnapshotName_Works() { // arrange string snapshotName = nameof(SnapshotTests) + "." + From 5cbc144fb0b1bf8a3671a1543da5ee901864db09 Mon Sep 17 00:00:00 2001 From: Tim Holzherr Date: Fri, 17 Mar 2023 16:47:54 +0100 Subject: [PATCH 8/9] Move FluentAssertion reference into its own package --- .../FluentAssertionsExtension.cs | 0 .../FluentSnapshotExtension.cs | 31 ++++++++++++++++ ...shooter.NUnit.TypedFluentAssertions.csproj | 23 ++++++++++++ src/Snapshooter.NUnit/SnapshotExtension.cs | 24 ------------ .../FluentAssertionsExtension.cs | 29 +++++++++++++++ .../FluentSnapshotExtension.cs | 31 ++++++++++++++++ ...shooter.Xunit.TypedFluentAssertions.csproj | 23 ++++++++++++ src/Snapshooter.Xunit/SnapshotExtension.cs | 24 ------------ src/Snapshooter.sln | 37 +++++++++++++++++++ src/Snapshooter/Snapshooter.csproj | 1 - .../Snapshooter.NUnit.Tests.csproj | 1 + .../Snapshooter.Xunit.Tests.csproj | 1 + 12 files changed, 176 insertions(+), 49 deletions(-) rename src/{Snapshooter/Extensions => Snapshooter.NUnit.TypedFluentAssertions}/FluentAssertionsExtension.cs (100%) create mode 100644 src/Snapshooter.NUnit.TypedFluentAssertions/FluentSnapshotExtension.cs create mode 100644 src/Snapshooter.NUnit.TypedFluentAssertions/Snapshooter.NUnit.TypedFluentAssertions.csproj create mode 100644 src/Snapshooter.Xunit.TypedFluentAssertions/FluentAssertionsExtension.cs create mode 100644 src/Snapshooter.Xunit.TypedFluentAssertions/FluentSnapshotExtension.cs create mode 100644 src/Snapshooter.Xunit.TypedFluentAssertions/Snapshooter.Xunit.TypedFluentAssertions.csproj diff --git a/src/Snapshooter/Extensions/FluentAssertionsExtension.cs b/src/Snapshooter.NUnit.TypedFluentAssertions/FluentAssertionsExtension.cs similarity index 100% rename from src/Snapshooter/Extensions/FluentAssertionsExtension.cs rename to src/Snapshooter.NUnit.TypedFluentAssertions/FluentAssertionsExtension.cs diff --git a/src/Snapshooter.NUnit.TypedFluentAssertions/FluentSnapshotExtension.cs b/src/Snapshooter.NUnit.TypedFluentAssertions/FluentSnapshotExtension.cs new file mode 100644 index 0000000..7412a8b --- /dev/null +++ b/src/Snapshooter.NUnit.TypedFluentAssertions/FluentSnapshotExtension.cs @@ -0,0 +1,31 @@ +using FluentAssertions; +using System; + +namespace Snapshooter.NUnit; + +public static class FluentSnapshotExtension +{ + /// + /// Creates a json snapshot of the given object and compares it with the + /// already existing snapshot of the test. + /// If no snapshot exists, a new snapshot will be created from the current result + /// and saved under a certain file path, which will shown within the test message. + /// + /// The object to match. + /// + /// Additional compare actions, which can be applied during the snapshot comparison + /// + public static void MatchSnapshot( + this TypedAssertions currentResult, + Func, MatchOptions>? matchOptions = null) + { + var cleanedObject = currentResult.RemoveUnwantedWrappers(); + + Func? chainedMatchOptions = + matchOptions != null ? m => matchOptions(new MatchOptions(m)) : null; + + Snapshot.Match( + cleanedObject, + chainedMatchOptions); + } +} diff --git a/src/Snapshooter.NUnit.TypedFluentAssertions/Snapshooter.NUnit.TypedFluentAssertions.csproj b/src/Snapshooter.NUnit.TypedFluentAssertions/Snapshooter.NUnit.TypedFluentAssertions.csproj new file mode 100644 index 0000000..9817bf0 --- /dev/null +++ b/src/Snapshooter.NUnit.TypedFluentAssertions/Snapshooter.NUnit.TypedFluentAssertions.csproj @@ -0,0 +1,23 @@ + + + + + Snapshooter.NUnit.TypedFluentAssertions + Snapshooter.NUnit.TypedFluentAssertions + Snapshooter.NUnit.TypedFluentAssertions + + TypedFluentAssertions is an extention to FluentAssertions and Snapshooter.NUnit. + It allows to configure snapshots in a type save and fluent way. + + false + + + + + + + + + + + \ No newline at end of file diff --git a/src/Snapshooter.NUnit/SnapshotExtension.cs b/src/Snapshooter.NUnit/SnapshotExtension.cs index f56b25d..a75271a 100644 --- a/src/Snapshooter.NUnit/SnapshotExtension.cs +++ b/src/Snapshooter.NUnit/SnapshotExtension.cs @@ -1,5 +1,4 @@ using System; -using FluentAssertions; namespace Snapshooter.NUnit { @@ -129,28 +128,5 @@ public static void MatchSnapshot( Snapshot.Match(cleanedObject, snapshotFullName, matchOptions); } - /// - /// Creates a json snapshot of the given object and compares it with the - /// already existing snapshot of the test. - /// If no snapshot exists, a new snapshot will be created from the current result - /// and saved under a certain file path, which will shown within the test message. - /// - /// The object to match. - /// - /// Additional compare actions, which can be applied during the snapshot comparison - /// - public static void MatchSnapshot( - this TypedAssertions currentResult, - Func, MatchOptions>? matchOptions = null) - { - var cleanedObject = currentResult.RemoveUnwantedWrappers(); - - Func? chainedMatchOptions = - matchOptions != null ? m => matchOptions(new MatchOptions(m)) : null; - - Snapshot.Match( - cleanedObject, - chainedMatchOptions); - } } } diff --git a/src/Snapshooter.Xunit.TypedFluentAssertions/FluentAssertionsExtension.cs b/src/Snapshooter.Xunit.TypedFluentAssertions/FluentAssertionsExtension.cs new file mode 100644 index 0000000..31bddef --- /dev/null +++ b/src/Snapshooter.Xunit.TypedFluentAssertions/FluentAssertionsExtension.cs @@ -0,0 +1,29 @@ +using FluentAssertions.Primitives; + +// Extension of Fluent assertion to not loose type information of asserted object +namespace FluentAssertions; + +/// +/// Contains a number of methods to assert that an is in the expected state. +/// +public class TypedAssertions : ObjectAssertions> +{ + internal TypedAssertions(T value) : base(value) + { + } +} + +/// +/// Contains extension methods for custom assertions in unit tests. +/// +public static class AssertionExtensions +{ + /// + /// Returns an object that can be used to assert the + /// current . + /// + public static TypedAssertions Should(this TSubject actualValue) + { + return new TypedAssertions(actualValue); + } +} diff --git a/src/Snapshooter.Xunit.TypedFluentAssertions/FluentSnapshotExtension.cs b/src/Snapshooter.Xunit.TypedFluentAssertions/FluentSnapshotExtension.cs new file mode 100644 index 0000000..461d90c --- /dev/null +++ b/src/Snapshooter.Xunit.TypedFluentAssertions/FluentSnapshotExtension.cs @@ -0,0 +1,31 @@ +using FluentAssertions; +using System; + +namespace Snapshooter.Xunit; + +public static class FluentSnapshotExtension +{ + /// + /// Creates a json snapshot of the given object and compares it with the + /// already existing snapshot of the test. + /// If no snapshot exists, a new snapshot will be created from the current result + /// and saved under a certain file path, which will shown within the test message. + /// + /// The object to match. + /// + /// Additional compare actions, which can be applied during the snapshot comparison + /// + public static void MatchSnapshot( + this TypedAssertions currentResult, + Func, MatchOptions>? matchOptions = null) + { + var cleanedObject = currentResult.RemoveUnwantedWrappers(); + + Func? chainedMatchOptions = + matchOptions != null ? m => matchOptions(new MatchOptions(m)) : null; + + Snapshot.Match( + cleanedObject, + chainedMatchOptions); + } +} diff --git a/src/Snapshooter.Xunit.TypedFluentAssertions/Snapshooter.Xunit.TypedFluentAssertions.csproj b/src/Snapshooter.Xunit.TypedFluentAssertions/Snapshooter.Xunit.TypedFluentAssertions.csproj new file mode 100644 index 0000000..47d5465 --- /dev/null +++ b/src/Snapshooter.Xunit.TypedFluentAssertions/Snapshooter.Xunit.TypedFluentAssertions.csproj @@ -0,0 +1,23 @@ + + + + + Snapshooter.Xunit.TypedFluentAssertions + Snapshooter.Xunit.TypedFluentAssertions + Snapshooter.Xunit.TypedFluentAssertions + + TypedFluentAssertions is an extention to FluentAssertions and Snapshooter.Xunit. + It allows to configure snapshots in a type save and fluent way. + + false + + + + + + + + + + + \ No newline at end of file diff --git a/src/Snapshooter.Xunit/SnapshotExtension.cs b/src/Snapshooter.Xunit/SnapshotExtension.cs index 308e225..9e7e9d4 100644 --- a/src/Snapshooter.Xunit/SnapshotExtension.cs +++ b/src/Snapshooter.Xunit/SnapshotExtension.cs @@ -1,5 +1,4 @@ using System; -using FluentAssertions; namespace Snapshooter.Xunit { @@ -129,28 +128,5 @@ public static void MatchSnapshot( Snapshot.Match(cleanedObject, snapshotFullName, matchOptions); } - /// - /// Creates a json snapshot of the given object and compares it with the - /// already existing snapshot of the test. - /// If no snapshot exists, a new snapshot will be created from the current result - /// and saved under a certain file path, which will shown within the test message. - /// - /// The object to match. - /// - /// Additional compare actions, which can be applied during the snapshot comparison - /// - public static void MatchSnapshot( - this TypedAssertions currentResult, - Func, MatchOptions>? matchOptions = null) - { - var cleanedObject = currentResult.RemoveUnwantedWrappers(); - - Func? chainedMatchOptions = - matchOptions != null ? m => matchOptions(new MatchOptions(m)) : null; - - Snapshot.Match( - cleanedObject, - chainedMatchOptions); - } } } diff --git a/src/Snapshooter.sln b/src/Snapshooter.sln index 0a8fe52..17469f3 100644 --- a/src/Snapshooter.sln +++ b/src/Snapshooter.sln @@ -32,6 +32,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snapshooter.Tests.Data", ". EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snapshooter.Xunit.Tests", "..\test\Snapshooter.Xunit.Tests\Snapshooter.Xunit.Tests.csproj", "{3C7A875E-7B9C-45E6-93E1-E952F08758B4}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{772D9217-4549-4DCE-ADFC-8AF209E5E9AC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snapshooter.Xunit.TypedFluentAssertions", "Snapshooter.Xunit.TypedFluentAssertions\Snapshooter.Xunit.TypedFluentAssertions.csproj", "{F3D664A5-1877-4623-ABC2-05D18412CF57}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snapshooter.NUnit.TypedFluentAssertions", "Snapshooter.NUnit.TypedFluentAssertions\Snapshooter.NUnit.TypedFluentAssertions.csproj", "{C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -198,11 +204,40 @@ Global {3C7A875E-7B9C-45E6-93E1-E952F08758B4}.Release|x64.Build.0 = Release|Any CPU {3C7A875E-7B9C-45E6-93E1-E952F08758B4}.Release|x86.ActiveCfg = Release|Any CPU {3C7A875E-7B9C-45E6-93E1-E952F08758B4}.Release|x86.Build.0 = Release|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Debug|x64.ActiveCfg = Debug|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Debug|x64.Build.0 = Debug|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Debug|x86.ActiveCfg = Debug|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Debug|x86.Build.0 = Debug|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Release|Any CPU.Build.0 = Release|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Release|x64.ActiveCfg = Release|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Release|x64.Build.0 = Release|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Release|x86.ActiveCfg = Release|Any CPU + {F3D664A5-1877-4623-ABC2-05D18412CF57}.Release|x86.Build.0 = Release|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Debug|x64.ActiveCfg = Debug|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Debug|x64.Build.0 = Debug|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Debug|x86.ActiveCfg = Debug|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Debug|x86.Build.0 = Debug|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Release|Any CPU.Build.0 = Release|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Release|x64.ActiveCfg = Release|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Release|x64.Build.0 = Release|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Release|x86.ActiveCfg = Release|Any CPU + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {D888581A-CCA4-485F-AED0-4D7B53D4BE54} = {772D9217-4549-4DCE-ADFC-8AF209E5E9AC} + {CE01C341-C674-4E4A-B490-BBFF354803C9} = {772D9217-4549-4DCE-ADFC-8AF209E5E9AC} + {679B413A-F26D-4F83-8BBF-5747B76567F8} = {772D9217-4549-4DCE-ADFC-8AF209E5E9AC} + {37A500BF-5B82-435F-9CC8-6D9313A7E3CC} = {772D9217-4549-4DCE-ADFC-8AF209E5E9AC} + {7AFA7B30-7F2F-420D-B510-D2880A00F975} = {772D9217-4549-4DCE-ADFC-8AF209E5E9AC} {5FCC450A-A047-48C7-8B3F-C5E973B5584D} = {45E598C5-3FD9-47E5-89D2-1F24A028F2BB} {C5859230-A6A7-45E7-993C-79C23527CB73} = {F9DFF684-4ACF-45E4-B23E-E8928DE0C9FE} {C2727126-A53A-4B13-AB09-559260F294D7} = {F9DFF684-4ACF-45E4-B23E-E8928DE0C9FE} @@ -211,6 +246,8 @@ Global {616F100E-A562-4E17-805A-8755B9D4D1AA} = {F9DFF684-4ACF-45E4-B23E-E8928DE0C9FE} {A9A09C8D-E9D1-45CC-80F1-3C8DDF8F2600} = {F9DFF684-4ACF-45E4-B23E-E8928DE0C9FE} {3C7A875E-7B9C-45E6-93E1-E952F08758B4} = {F9DFF684-4ACF-45E4-B23E-E8928DE0C9FE} + {F3D664A5-1877-4623-ABC2-05D18412CF57} = {772D9217-4549-4DCE-ADFC-8AF209E5E9AC} + {C16AB9AB-4F11-4A5A-BDBD-01ECD5D3BF19} = {772D9217-4549-4DCE-ADFC-8AF209E5E9AC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2F64A2AB-ACA2-4E2D-B7E2-B87E93C66A24} diff --git a/src/Snapshooter/Snapshooter.csproj b/src/Snapshooter/Snapshooter.csproj index 3ca191b..a6b1936 100644 --- a/src/Snapshooter/Snapshooter.csproj +++ b/src/Snapshooter/Snapshooter.csproj @@ -14,7 +14,6 @@ - diff --git a/test/Snapshooter.NUnit.Tests/Snapshooter.NUnit.Tests.csproj b/test/Snapshooter.NUnit.Tests/Snapshooter.NUnit.Tests.csproj index 61ebced..87239ff 100644 --- a/test/Snapshooter.NUnit.Tests/Snapshooter.NUnit.Tests.csproj +++ b/test/Snapshooter.NUnit.Tests/Snapshooter.NUnit.Tests.csproj @@ -14,6 +14,7 @@ + diff --git a/test/Snapshooter.Xunit.Tests/Snapshooter.Xunit.Tests.csproj b/test/Snapshooter.Xunit.Tests/Snapshooter.Xunit.Tests.csproj index cb6feff..d01166b 100644 --- a/test/Snapshooter.Xunit.Tests/Snapshooter.Xunit.Tests.csproj +++ b/test/Snapshooter.Xunit.Tests/Snapshooter.Xunit.Tests.csproj @@ -10,6 +10,7 @@ + From 21c9d8fce841b08a1bbb5da3c07ff123a5db6e4d Mon Sep 17 00:00:00 2001 From: Tim Holzherr Date: Fri, 17 Mar 2023 16:50:43 +0100 Subject: [PATCH 9/9] fix whitespace --- src/Snapshooter.NUnit/SnapshotExtension.cs | 1 - src/Snapshooter.Xunit/SnapshotExtension.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Snapshooter.NUnit/SnapshotExtension.cs b/src/Snapshooter.NUnit/SnapshotExtension.cs index a75271a..b57d5f8 100644 --- a/src/Snapshooter.NUnit/SnapshotExtension.cs +++ b/src/Snapshooter.NUnit/SnapshotExtension.cs @@ -127,6 +127,5 @@ public static void MatchSnapshot( var cleanedObject = currentResult.RemoveUnwantedWrappers(); Snapshot.Match(cleanedObject, snapshotFullName, matchOptions); } - } } diff --git a/src/Snapshooter.Xunit/SnapshotExtension.cs b/src/Snapshooter.Xunit/SnapshotExtension.cs index 9e7e9d4..afdff1c 100644 --- a/src/Snapshooter.Xunit/SnapshotExtension.cs +++ b/src/Snapshooter.Xunit/SnapshotExtension.cs @@ -127,6 +127,5 @@ public static void MatchSnapshot( var cleanedObject = currentResult.RemoveUnwantedWrappers(); Snapshot.Match(cleanedObject, snapshotFullName, matchOptions); } - } }