From 80d46a67221bb1deb79a058e2c81f8a84ad1a7c6 Mon Sep 17 00:00:00 2001 From: luetm Date: Wed, 21 Feb 2024 14:03:35 +0100 Subject: [PATCH 01/11] Created tests for the first to features in the README. --- Blazored.FluentValidation.sln | 17 +++ README.md | 2 +- .../AssemblyScanning/Component.razor | 34 ++++++ .../AssemblyScanning/Readme.md | 7 ++ .../AssemblyScanning/Tests.cs | 64 ++++++++++ .../BasicValidation/Component.razor | 44 +++++++ .../BasicValidation/Readme.md | 7 ++ .../BasicValidation/Tests.cs | 110 ++++++++++++++++++ .../Blazored.FluentValidation.Tests.csproj | 41 +++++++ .../GlobalUsings.cs | 3 + .../Model/Address.cs | 27 +++++ .../Model/Person.cs | 58 +++++++++ .../Model/ValidationResultType.cs | 8 ++ .../_Imports.razor | 14 +++ 14 files changed, 435 insertions(+), 1 deletion(-) create mode 100644 tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor create mode 100644 tests/Blazored.FluentValidation.Tests/AssemblyScanning/Readme.md create mode 100644 tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs create mode 100644 tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor create mode 100644 tests/Blazored.FluentValidation.Tests/BasicValidation/Readme.md create mode 100644 tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs create mode 100644 tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj create mode 100644 tests/Blazored.FluentValidation.Tests/GlobalUsings.cs create mode 100644 tests/Blazored.FluentValidation.Tests/Model/Address.cs create mode 100644 tests/Blazored.FluentValidation.Tests/Model/Person.cs create mode 100644 tests/Blazored.FluentValidation.Tests/Model/ValidationResultType.cs create mode 100644 tests/Blazored.FluentValidation.Tests/_Imports.razor diff --git a/Blazored.FluentValidation.sln b/Blazored.FluentValidation.sln index 5b8ce3e..bc1abab 100644 --- a/Blazored.FluentValidation.sln +++ b/Blazored.FluentValidation.sln @@ -20,6 +20,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorServer", "samples\Bla EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedModels", "samples\Shared\SharedModels\SharedModels.csproj", "{42276235-5139-41D6-923D-18B7EB5E3E44}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{DACAA0DB-2B93-4FE1-9D21-F45A4E63A640}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazored.FluentValidation.Tests", "tests\Blazored.FluentValidation.Tests\Blazored.FluentValidation.Tests.csproj", "{C92DF59B-B760-4FCC-A34C-A4007529BCC5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -78,6 +82,18 @@ Global {42276235-5139-41D6-923D-18B7EB5E3E44}.Release|x64.Build.0 = Release|Any CPU {42276235-5139-41D6-923D-18B7EB5E3E44}.Release|x86.ActiveCfg = Release|Any CPU {42276235-5139-41D6-923D-18B7EB5E3E44}.Release|x86.Build.0 = Release|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Debug|x64.ActiveCfg = Debug|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Debug|x64.Build.0 = Debug|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Debug|x86.ActiveCfg = Debug|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Debug|x86.Build.0 = Debug|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Release|Any CPU.Build.0 = Release|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Release|x64.ActiveCfg = Release|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Release|x64.Build.0 = Release|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Release|x86.ActiveCfg = Release|Any CPU + {C92DF59B-B760-4FCC-A34C-A4007529BCC5}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -86,6 +102,7 @@ Global {8BC1065A-A71E-4568-8A67-9C3AF039F73A} = {D5C6DCA9-C2BD-4117-BCCC-19E36E8406AB} {2459CF4B-6548-4031-B784-43E943E270A9} = {D5C6DCA9-C2BD-4117-BCCC-19E36E8406AB} {42276235-5139-41D6-923D-18B7EB5E3E44} = {D5C6DCA9-C2BD-4117-BCCC-19E36E8406AB} + {C92DF59B-B760-4FCC-A34C-A4007529BCC5} = {DACAA0DB-2B93-4FE1-9D21-F45A4E63A640} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {42B22D99-6E59-4B30-88AD-B9CC07E0DA49} diff --git a/README.md b/README.md index 02b1e46..1ce13f1 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ The second is when manually validating the model using the `Validate` or `Valida ``` ## Access to full `ValidationFailure` -If you need details about the specifics of a validation result (e.g. its `Severity), you can access the result of the +If you need details about the specifics of a validation result (e.g. its `Severity`), you can access the result of the last validation by calling the `GetFailuresFromLastValidation` method on the `FluentValidationValidator` component. ```razor diff --git a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor new file mode 100644 index 0000000..476cd48 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor @@ -0,0 +1,34 @@ +@using Blazored.FluentValidation.Tests.Model + + + @if (DisableAssemblyScanning is null) + { + + } + else + { + + } + + + +

+ + +

+ + +
+ +@code { + [Parameter] public bool? DisableAssemblyScanning { get; set; } + private readonly Person _person = new(); + + internal ValidationResultType Result { get; private set; } = ValidationResultType.Valid; + + private void ValidSubmit() => Result = ValidationResultType.Valid; + private void InvalidSubmit() => Result = ValidationResultType.Error; +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Readme.md b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Readme.md new file mode 100644 index 0000000..a332f37 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Readme.md @@ -0,0 +1,7 @@ +### What does this test? +This test checks if the assembly scanning works. It leverages, that this test +assembly does not register any `AbstractValidator` by default. + + - Setting the `DisableAssemblyScanning` to `true` should not find any validators and ignore errors. + - Setting the `DisableAssemblyScanning` to `false` or not setting the attribute at all, should + find the validators in the assembly and validate normally. diff --git a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs new file mode 100644 index 0000000..7f4ba4c --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs @@ -0,0 +1,64 @@ +using Blazored.FluentValidation.Tests.Model; + +namespace Blazored.FluentValidation.Tests.AssemblyScanning; + +public class Tests : TestContext +{ + private readonly Fixture _fixture = new(); + + [Fact] + public void DisableAssemblyScanning_SetToTrue_NoValidationHappens() + { + // Arrange + var cut = RenderComponent(p => p.Add(c => c.DisableAssemblyScanning, true)); + var person = _fixture.InvalidPerson(); + + // Act + cut.Find($"input[name={nameof(Person.FirstName)}]").Change(person.FirstName); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Valid); + } + + [Fact] + public void DisableAssemblyScanning_SetToFalse_ValidationHappens() + { + // Arrange + var cut = RenderComponent(p => p.Add(c => c.DisableAssemblyScanning, false)); + var person = _fixture.InvalidPerson(); + + // Act + cut.Find($"input[name={nameof(Person.FirstName)}]").Change(person.FirstName); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Error); + } + + [Fact] + public void DisableAssemblyScanning_NotSet_ValidationHappens() + { + // Arrange + var cut = RenderComponent(p => p.Add(c => c.DisableAssemblyScanning, null)); + var person = _fixture.InvalidPerson(); + + // Act + cut.Find($"input[name={nameof(Person.FirstName)}]").Change(person.FirstName); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Error); + } + + private class Fixture + { + public Person InvalidPerson() => new() + { + FirstName = "", + LastName = "Doe", + EmailAddress = "john.doe@blazored.org", + Age = 30 + }; + } +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor b/tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor new file mode 100644 index 0000000..7a12405 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor @@ -0,0 +1,44 @@ +@using Blazored.FluentValidation.Tests.Model + + + + + +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ + +
+ +@code { + private readonly Person _person = new(); + internal ValidationResultType Result { get; private set; } = ValidationResultType.Valid; + + private void ValidSubmit() => Result = ValidationResultType.Valid; + private void InvalidSubmit() => Result = ValidationResultType.Error; +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/BasicValidation/Readme.md b/tests/Blazored.FluentValidation.Tests/BasicValidation/Readme.md new file mode 100644 index 0000000..4ab205e --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/BasicValidation/Readme.md @@ -0,0 +1,7 @@ +### What does this test? +This test checks if the basic validation works. + + - Does a valid model pass the validation? + - Do basic validation rules get picked up? + - Do validation errors get displayed correctly in the UI? + - Are nested rules validated correctly? diff --git a/tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs b/tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs new file mode 100644 index 0000000..ae461af --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs @@ -0,0 +1,110 @@ +using Blazored.FluentValidation.Tests.Model; + +namespace Blazored.FluentValidation.Tests.BasicValidation; + +public class Tests : TestContext +{ + private readonly Fixture _fixture = new(); + + [Fact] + public void Validate_DataIsValid_ValidSubmit() + { + // Arrange + var cut = RenderComponent(); + var person = _fixture.ValidPerson(); + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Valid); + } + + [Fact] + public void Validate_FirstNameMissing_InvalidSubmit() + { + // Arrange + var cut = RenderComponent(); + var person = _fixture.ValidPerson() with { FirstName = string.Empty }; + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Error); + } + + [Fact] + public void Validate_FirstNameMissing_ValidationErrorsPresent() + { + // Arrange + var cut = RenderComponent(); + var person = _fixture.ValidPerson() with { FirstName = string.Empty }; + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Find(".validation-errors>.validation-message").TextContent.Should().Contain(PersonValidator.FirstNameRequired); + cut.Find("li.validation-message").TextContent.Should().Contain(PersonValidator.FirstNameRequired); + } + + [Fact] + public void Validate_AgeTooOld_ValidationErrorsPresent() + { + // Arrange + var cut = RenderComponent(); + var person = _fixture.ValidPerson() with { Age = 250 }; + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Find(".validation-errors>.validation-message").TextContent.Should().Contain(PersonValidator.AgeMax); + } + + [Fact] + public void Validate_AddressLine1Missing_ValidationErrorsPresent() + { + // Arrange + var cut = RenderComponent(); + var person = _fixture.ValidPerson() with { Address = new() { Line1 = string.Empty } }; + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Find(".validation-errors>.validation-message").TextContent.Should().Contain(AddressValidator.Line1Required); + } + + private static void FillForm(IRenderedComponent cut, Person person) + { + cut.Find($"input[name={nameof(Person.FirstName)}]").Change(person.FirstName); + cut.Find($"input[name={nameof(Person.LastName)}]").Change(person.LastName); + cut.Find($"input[name={nameof(Person.EmailAddress)}]").Change(person.EmailAddress); + cut.Find($"input[name={nameof(Person.Age)}]").Change(person.Age.ToString()); + cut.Find($"input[name={nameof(Person.Address.Line1)}]").Change(person.Address.Line1); + } + + private class Fixture + { + public Person ValidPerson() => new() + { + FirstName = "John", + LastName = "Doe", + EmailAddress = "john.doe@blazored.org", + Age = 30, + Address = new() + { + Line1 = "123 Main St", + Town = "Springfield", + Postcode = "12345" + } + }; + } +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj new file mode 100644 index 0000000..8269342 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj @@ -0,0 +1,41 @@ + + + + net7.0 + enable + false + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + diff --git a/tests/Blazored.FluentValidation.Tests/GlobalUsings.cs b/tests/Blazored.FluentValidation.Tests/GlobalUsings.cs new file mode 100644 index 0000000..6b9b9b4 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using System.Threading.Tasks; +global using FluentAssertions; +global using FluentValidation; \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/Model/Address.cs b/tests/Blazored.FluentValidation.Tests/Model/Address.cs new file mode 100644 index 0000000..d7a5332 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/Model/Address.cs @@ -0,0 +1,27 @@ +namespace Blazored.FluentValidation.Tests.Model +{ + public record Address + { + public string? Line1 { get; set; } + public string? Line2 { get; set; } + public string? Town { get; set; } + public string? County { get; set; } + public string? Postcode { get; set; } + } + + public class AddressValidator : AbstractValidator
+ { + public const string Line1Required = "You must enter Line 1"; + public const string TownRequired = "You must enter a town"; + public const string CountyRequired = "You must enter a county"; + public const string PostcodeRequired = "You must enter a postcode"; + + public AddressValidator() + { + RuleFor(p => p.Line1).NotEmpty().WithMessage(Line1Required); + RuleFor(p => p.Town).NotEmpty().WithMessage(TownRequired); + RuleFor(p => p.County).NotEmpty().WithMessage(CountyRequired); + RuleFor(p => p.Postcode).NotEmpty().WithMessage(PostcodeRequired); + } + } +} diff --git a/tests/Blazored.FluentValidation.Tests/Model/Person.cs b/tests/Blazored.FluentValidation.Tests/Model/Person.cs new file mode 100644 index 0000000..e84b1b7 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/Model/Person.cs @@ -0,0 +1,58 @@ +namespace Blazored.FluentValidation.Tests.Model +{ + public record Person + { + public string? FirstName { get; set; } + public string? LastName { get; set; } + public int? Age { get; set; } + public string? EmailAddress { get; set; } + public Address Address { get; set; } = new(); + } + + public class PersonValidator : AbstractValidator + { + public const string FirstNameRequired = "You must enter your first name"; + public const string FirstNameMaxLength = "First name cannot be longer than 50 characters"; + public const string LastNameRequired = "You must enter your last name"; + public const string LastNameMaxLength = "Last name cannot be longer than 50 characters"; + public const string AgeRequired = "You must enter your age"; + public const string AgeMin = "Age must be greater than 0"; + public const string AgeMax = "Age cannot be greater than 150"; + public const string EmailRequired = "You must enter an email address"; + public const string EmailValid = "You must provide a valid email address"; + public const string EmailUnique = "Email address must be unique"; + public const string DuplicateEmail = "mail@my.com"; + + public PersonValidator() + { + RuleSet("Names", () => + { + RuleFor(p => p.FirstName) + .NotEmpty().WithMessage(FirstNameRequired) + .MaximumLength(50).WithMessage(FirstNameMaxLength); + + RuleFor(p => p.LastName) + .NotEmpty().WithMessage(LastNameRequired) + .MaximumLength(50).WithMessage(LastNameMaxLength); + }); + + RuleFor(p => p.Age) + .NotNull().WithMessage(AgeRequired) + .GreaterThanOrEqualTo(0).WithMessage(AgeMin) + .LessThan(150).WithMessage(AgeMax); + + RuleFor(p => p.EmailAddress) + .NotEmpty().WithMessage(EmailRequired) + .EmailAddress().WithMessage(EmailValid) + .MustAsync(async (email, _) => await IsUniqueAsync(email)).WithMessage(EmailUnique); + + RuleFor(p => p.Address).SetValidator(new AddressValidator()); + } + + private static async Task IsUniqueAsync(string? email) + { + await Task.Delay(300); + return email?.ToLower() != DuplicateEmail; + } + } +} diff --git a/tests/Blazored.FluentValidation.Tests/Model/ValidationResultType.cs b/tests/Blazored.FluentValidation.Tests/Model/ValidationResultType.cs new file mode 100644 index 0000000..e35c667 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/Model/ValidationResultType.cs @@ -0,0 +1,8 @@ +namespace Blazored.FluentValidation.Tests.Model; + +public enum ValidationResultType +{ + Valid, + Warning, + Error +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/_Imports.razor b/tests/Blazored.FluentValidation.Tests/_Imports.razor new file mode 100644 index 0000000..6d4953f --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/_Imports.razor @@ -0,0 +1,14 @@ +@using Blazored.FluentValidation + +@using Bunit +@using Bunit.TestDoubles + +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.Extensions.DependencyInjection +@using Microsoft.JSInterop +@using System.Net.Http +@using System.Net.Http.Json + +@using Xunit \ No newline at end of file From 8071461b0b5a3376ae7a4d5859ff6060d5a77629 Mon Sep 17 00:00:00 2001 From: luetm Date: Wed, 21 Feb 2024 14:11:12 +0100 Subject: [PATCH 02/11] Added IoC test case to assembly scanning tests. --- .../AssemblyScanning/Readme.md | 2 ++ .../AssemblyScanning/Tests.cs | 16 ++++++++++++++++ .../Blazored.FluentValidation.Tests.csproj | 4 ++++ 3 files changed, 22 insertions(+) diff --git a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Readme.md b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Readme.md index a332f37..4540b8d 100644 --- a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Readme.md +++ b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Readme.md @@ -5,3 +5,5 @@ assembly does not register any `AbstractValidator` by default. - Setting the `DisableAssemblyScanning` to `true` should not find any validators and ignore errors. - Setting the `DisableAssemblyScanning` to `false` or not setting the attribute at all, should find the validators in the assembly and validate normally. + - Setting the `DisableAssemblyScanning` to `true` and registering the validators manually should + find the validators and validate normally. diff --git a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs index 7f4ba4c..32be18a 100644 --- a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs +++ b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs @@ -51,6 +51,22 @@ public void DisableAssemblyScanning_NotSet_ValidationHappens() cut.Instance.Result.Should().Be(ValidationResultType.Error); } + [Fact] + public void DisableAssemblyScanning_SetToTrueButValidatorsRegistered_ValidationHappens() + { + // Arrange + Services.AddTransient, PersonValidator>(); + var cut = RenderComponent(p => p.Add(c => c.DisableAssemblyScanning, null)); + var person = _fixture.InvalidPerson(); + + // Act + cut.Find($"input[name={nameof(Person.FirstName)}]").Change(person.FirstName); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Error); + } + private class Fixture { public Person InvalidPerson() => new() diff --git a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj index 8269342..b52f9e7 100644 --- a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj +++ b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj @@ -36,6 +36,10 @@ + + + + From 12eb3d2fc59d31d46eadd700d26e9782c0941681 Mon Sep 17 00:00:00 2001 From: luetm Date: Wed, 21 Feb 2024 14:28:13 +0100 Subject: [PATCH 03/11] Added some `ValidateAsync` tests, one is failing for some reason, need to investigate. --- .../AssemblyScanning/Component.razor | 3 +- .../AsyncValidation/Component.razor | 41 ++++++++++ .../AsyncValidation/Readme.md | 1 + .../AsyncValidation/Tests.cs | 80 +++++++++++++++++++ .../BasicValidation/Component.razor | 3 +- .../Blazored.FluentValidation.Tests.csproj | 4 - .../_Imports.razor | 1 + 7 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor create mode 100644 tests/Blazored.FluentValidation.Tests/AsyncValidation/Readme.md create mode 100644 tests/Blazored.FluentValidation.Tests/AsyncValidation/Tests.cs diff --git a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor index 476cd48..e501004 100644 --- a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor +++ b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor @@ -1,5 +1,4 @@ -@using Blazored.FluentValidation.Tests.Model - diff --git a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor new file mode 100644 index 0000000..9889587 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor @@ -0,0 +1,41 @@ + + + + +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ + + +
+ +@code { + private readonly Person _person = new(); + private FluentValidationValidator? _fluentValidationValidator; + + public ValidationResultType Result { get; private set; } + + private async Task SubmitFormAsync() + { + var result = await _fluentValidationValidator!.ValidateAsync(); + Result = result ? ValidationResultType.Valid : ValidationResultType.Error; + } + +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Readme.md b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Readme.md new file mode 100644 index 0000000..3e49f9c --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Readme.md @@ -0,0 +1 @@ +### What does this test? diff --git a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Tests.cs b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Tests.cs new file mode 100644 index 0000000..1556ac1 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Tests.cs @@ -0,0 +1,80 @@ +using Blazored.FluentValidation.Tests.Model; + +namespace Blazored.FluentValidation.Tests.AsyncValidation; + +public class Tests : TestContext +{ + private readonly Fixture _fixture = new(); + + [Fact] + public void AsyncValidate_PersonIsValid_ResultIsValid() + { + // Arrange + var cut = RenderComponent(); + var person = _fixture.ValidPerson(); + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Valid); + } + + [Fact] + public void AsyncValidate_FirstNameTooLong_ResultIsError() + { + // Arrange + var cut = RenderComponent(); + var person = _fixture.ValidPerson() with + { + FirstName = "This is a very long first name that is over 50 characters long" + }; + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Error); + } + + [Fact] + public void AsyncValidate_FirstNameTooLong_ValidationMessagesPresent() + { + // Arrange + var cut = RenderComponent(); + var person = _fixture.ValidPerson() with + { + FirstName = "This is a very long first name that is over 50 characters long", + LastName = "", + }; + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Find(".validation-errors>.validation-message").TextContent.Should().Contain(PersonValidator.FirstNameMaxLength); + cut.Find("li.validation-message").TextContent.Should().Contain(PersonValidator.FirstNameMaxLength); + } + + private void FillForm(IRenderedComponent cut, Person person) + { + cut.Find("input[name=FirstName]").Change(person.FirstName); + cut.Find("input[name=LastName]").Change(person.LastName); + cut.Find("input[name=Age]").Change(person.Age.ToString()); + cut.Find("input[name=EmailAddress]").Change(person.EmailAddress); + } + + private class Fixture + { + public Person ValidPerson() => new() + { + FirstName = "John", + LastName = "Doe", + Age = 30, + EmailAddress = "john.doe@blazored.com" + }; + } +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor b/tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor index 7a12405..82093d8 100644 --- a/tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor +++ b/tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor @@ -1,5 +1,4 @@ -@using Blazored.FluentValidation.Tests.Model - diff --git a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj index b52f9e7..8269342 100644 --- a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj +++ b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj @@ -36,10 +36,6 @@ - - - - diff --git a/tests/Blazored.FluentValidation.Tests/_Imports.razor b/tests/Blazored.FluentValidation.Tests/_Imports.razor index 6d4953f..b5e1119 100644 --- a/tests/Blazored.FluentValidation.Tests/_Imports.razor +++ b/tests/Blazored.FluentValidation.Tests/_Imports.razor @@ -1,4 +1,5 @@ @using Blazored.FluentValidation +@using Blazored.FluentValidation.Tests.Model @using Bunit @using Bunit.TestDoubles From 1e11c18cc90285f760b5b31bccbdb1c46a529270 Mon Sep 17 00:00:00 2001 From: luetm Date: Wed, 21 Feb 2024 14:53:46 +0100 Subject: [PATCH 04/11] Test now passes, however the behavior is strange: When doing normal validation, all rules are automatically included, but when doing it with `ValidateAsync`, they need to be included manually. --- .../AsyncValidation/Component.razor | 3 +- .../AsyncValidation/Tests.cs | 29 ++++++++----------- .../BasicValidation/Component.razor | 4 +-- .../BasicValidation/Tests.cs | 2 +- .../Model/Person.cs | 6 ++-- 5 files changed, 20 insertions(+), 24 deletions(-) diff --git a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor index 9889587..ff4f9eb 100644 --- a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor +++ b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor @@ -30,12 +30,11 @@ private readonly Person _person = new(); private FluentValidationValidator? _fluentValidationValidator; - public ValidationResultType Result { get; private set; } + public ValidationResultType? Result { get; private set; } private async Task SubmitFormAsync() { var result = await _fluentValidationValidator!.ValidateAsync(); Result = result ? ValidationResultType.Valid : ValidationResultType.Error; } - } \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Tests.cs b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Tests.cs index 1556ac1..453cec4 100644 --- a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Tests.cs +++ b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Tests.cs @@ -1,11 +1,10 @@ using Blazored.FluentValidation.Tests.Model; - namespace Blazored.FluentValidation.Tests.AsyncValidation; public class Tests : TestContext { private readonly Fixture _fixture = new(); - + [Fact] public void AsyncValidate_PersonIsValid_ResultIsValid() { @@ -16,47 +15,43 @@ public void AsyncValidate_PersonIsValid_ResultIsValid() // Act FillForm(cut, person); cut.Find("button").Click(); + cut.WaitForState(() => cut.Instance.Result is not null); // Assert cut.Instance.Result.Should().Be(ValidationResultType.Valid); } - + [Fact] - public void AsyncValidate_FirstNameTooLong_ResultIsError() + public void AsyncValidate_AgeNegative_ResultIsError() { // Arrange var cut = RenderComponent(); - var person = _fixture.ValidPerson() with - { - FirstName = "This is a very long first name that is over 50 characters long" - }; + var person = _fixture.ValidPerson() with { Age = -5 }; // Act FillForm(cut, person); cut.Find("button").Click(); + cut.WaitForState(() => cut.Instance.Result is not null); // Assert cut.Instance.Result.Should().Be(ValidationResultType.Error); } - + [Fact] - public void AsyncValidate_FirstNameTooLong_ValidationMessagesPresent() + public void AsyncValidate_AgeNegative_ValidationMessagesPresent() { // Arrange var cut = RenderComponent(); - var person = _fixture.ValidPerson() with - { - FirstName = "This is a very long first name that is over 50 characters long", - LastName = "", - }; + var person = _fixture.ValidPerson() with { Age = -5 }; // Act FillForm(cut, person); cut.Find("button").Click(); + cut.WaitForState(() => cut.Instance.Result is not null); // Assert - cut.Find(".validation-errors>.validation-message").TextContent.Should().Contain(PersonValidator.FirstNameMaxLength); - cut.Find("li.validation-message").TextContent.Should().Contain(PersonValidator.FirstNameMaxLength); + cut.Find(".validation-errors>.validation-message").TextContent.Should().Contain(PersonValidator.AgeMin); + cut.Find("li.validation-message").TextContent.Should().Contain(PersonValidator.AgeMin); } private void FillForm(IRenderedComponent cut, Person person) diff --git a/tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor b/tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor index 82093d8..17b2f0f 100644 --- a/tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor +++ b/tests/Blazored.FluentValidation.Tests/BasicValidation/Component.razor @@ -28,14 +28,14 @@

- +

@code { - private readonly Person _person = new(); + private readonly Person _person = new() { Address = new() }; internal ValidationResultType Result { get; private set; } = ValidationResultType.Valid; private void ValidSubmit() => Result = ValidationResultType.Valid; diff --git a/tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs b/tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs index ae461af..fdcef88 100644 --- a/tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs +++ b/tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs @@ -88,7 +88,7 @@ private static void FillForm(IRenderedComponent cut, Person person) cut.Find($"input[name={nameof(Person.LastName)}]").Change(person.LastName); cut.Find($"input[name={nameof(Person.EmailAddress)}]").Change(person.EmailAddress); cut.Find($"input[name={nameof(Person.Age)}]").Change(person.Age.ToString()); - cut.Find($"input[name={nameof(Person.Address.Line1)}]").Change(person.Address.Line1); + cut.Find($"input[name={nameof(Person.Address.Line1)}]").Change(person.Address!.Line1); } private class Fixture diff --git a/tests/Blazored.FluentValidation.Tests/Model/Person.cs b/tests/Blazored.FluentValidation.Tests/Model/Person.cs index e84b1b7..93a61a1 100644 --- a/tests/Blazored.FluentValidation.Tests/Model/Person.cs +++ b/tests/Blazored.FluentValidation.Tests/Model/Person.cs @@ -6,7 +6,7 @@ public record Person public string? LastName { get; set; } public int? Age { get; set; } public string? EmailAddress { get; set; } - public Address Address { get; set; } = new(); + public Address? Address { get; set; } } public class PersonValidator : AbstractValidator @@ -46,7 +46,9 @@ public PersonValidator() .EmailAddress().WithMessage(EmailValid) .MustAsync(async (email, _) => await IsUniqueAsync(email)).WithMessage(EmailUnique); - RuleFor(p => p.Address).SetValidator(new AddressValidator()); + RuleFor(p => p.Address!) + .SetValidator(new AddressValidator()) + .When(p => p.Address is not null); } private static async Task IsUniqueAsync(string? email) From 0771d422e885a15b5bafdc8bd083764ca9aad80b Mon Sep 17 00:00:00 2001 From: luetm Date: Wed, 21 Feb 2024 14:58:18 +0100 Subject: [PATCH 05/11] Forgot Readme.md. --- .../Blazored.FluentValidation.Tests/AsyncValidation/Readme.md | 3 +++ .../Blazored.FluentValidation.Tests.csproj | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Readme.md b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Readme.md index 3e49f9c..e1363a0 100644 --- a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Readme.md +++ b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Readme.md @@ -1 +1,4 @@ ### What does this test? +This test checks if the `ValidateAsync` method works correctly, +specifically that the `bool` returned is `false` when validation fails, +and `true` otherwise. \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj index 8269342..dbfbe06 100644 --- a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj +++ b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj @@ -36,6 +36,10 @@ + + + + From 7d2df1b7702112cbdc27f765beb17452d8b11587 Mon Sep 17 00:00:00 2001 From: luetm Date: Wed, 21 Feb 2024 15:46:09 +0100 Subject: [PATCH 06/11] Tests for rule sets (.razor variant bugged?) --- .../AsyncValidation/Component.razor | 1 - .../Blazored.FluentValidation.Tests.csproj | 4 -- .../RuleSets/Component.razor | 37 ++++++++++++++ .../RuleSets/Tests.cs | 50 +++++++++++++++++++ 4 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 tests/Blazored.FluentValidation.Tests/RuleSets/Component.razor create mode 100644 tests/Blazored.FluentValidation.Tests/RuleSets/Tests.cs diff --git a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor index ff4f9eb..2acb1a0 100644 --- a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor +++ b/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor @@ -23,7 +23,6 @@

-
@code { diff --git a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj index dbfbe06..8269342 100644 --- a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj +++ b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj @@ -36,10 +36,6 @@ - - - - diff --git a/tests/Blazored.FluentValidation.Tests/RuleSets/Component.razor b/tests/Blazored.FluentValidation.Tests/RuleSets/Component.razor new file mode 100644 index 0000000..e8b633b --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/RuleSets/Component.razor @@ -0,0 +1,37 @@ + + + @if (IncludeWithAttribute) + { + @*

Can't implement.. bugged?

*@ + } + else + { + + } + + +

+ + +

+ + +
+ +@code { + [Parameter] public bool IncludeWithAttribute { get; set; } + [Parameter] public bool IncludeWithCode { get; set; } + + private readonly Person _person = new(); + private FluentValidationValidator? _fluentValidationValidator; + + public ValidationResultType? Result { get; private set; } + + protected void Submit() + { + var result = _fluentValidationValidator!.Validate(o => o.IncludeRuleSets("Names")); + Result = result ? ValidationResultType.Valid : ValidationResultType.Error; + } +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/RuleSets/Tests.cs b/tests/Blazored.FluentValidation.Tests/RuleSets/Tests.cs new file mode 100644 index 0000000..dc8d5c7 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/RuleSets/Tests.cs @@ -0,0 +1,50 @@ +using Blazored.FluentValidation.Tests.Model; + +namespace Blazored.FluentValidation.Tests.RuleSets; + +public class Tests : TestContext +{ + private readonly Fixture _fixture = new(); + + [Fact] + public void AddedByAttribute_PersonFirstNameTooLong_ValidationFails() + { + // Arrange + var person = _fixture.ValidPerson() with + { + FirstName = "This name is clearly longer than 50 characters and thus should fail." + }; + var cut = RenderComponent(p => p.Add(c => c.IncludeWithCode, true)); + + // Act + cut.Find("input[name=FirstName]").Change(person.FirstName); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Error); + } + + [Fact] + public void AddedByAttribute_PersonFirstNameTooLong_ValidationMessagesPresent() + { + // Arrange + var person = _fixture.ValidPerson() with + { + FirstName = "This name is clearly longer than 50 characters and thus should fail." + }; + var cut = RenderComponent(p => p.Add(c => c.IncludeWithCode, true)); + + // Act + cut.Find("input[name=FirstName]").Change(person.FirstName); + cut.Find("button").Click(); + + // Assert + cut.Find(".validation-errors>.validation-message").TextContent.Should().Contain(PersonValidator.FirstNameMaxLength); + cut.Find("li.validation-message").TextContent.Should().Contain(PersonValidator.FirstNameMaxLength); + } + + private class Fixture + { + public Person ValidPerson() => new() { FirstName = "John" }; + } +} \ No newline at end of file From 7f4c4eb8536f52f429b12680b1390d62a4737288 Mon Sep 17 00:00:00 2001 From: luetm Date: Wed, 21 Feb 2024 15:57:14 +0100 Subject: [PATCH 07/11] Re-added code I destroyed. --- .../RuleSets/Component.razor | 22 +++++++-- .../RuleSets/Tests.cs | 45 +++++++++++++++---- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/tests/Blazored.FluentValidation.Tests/RuleSets/Component.razor b/tests/Blazored.FluentValidation.Tests/RuleSets/Component.razor index e8b633b..fa209c7 100644 --- a/tests/Blazored.FluentValidation.Tests/RuleSets/Component.razor +++ b/tests/Blazored.FluentValidation.Tests/RuleSets/Component.razor @@ -1,7 +1,7 @@  - + @if (IncludeWithAttribute) { @*

Can't implement.. bugged?

*@ @@ -17,21 +17,37 @@

+

+ + +

+ +

+ + +

+ +

+ + +

+
@code { [Parameter] public bool IncludeWithAttribute { get; set; } [Parameter] public bool IncludeWithCode { get; set; } - + private readonly Person _person = new(); private FluentValidationValidator? _fluentValidationValidator; public ValidationResultType? Result { get; private set; } - + protected void Submit() { var result = _fluentValidationValidator!.Validate(o => o.IncludeRuleSets("Names")); Result = result ? ValidationResultType.Valid : ValidationResultType.Error; } + } \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/RuleSets/Tests.cs b/tests/Blazored.FluentValidation.Tests/RuleSets/Tests.cs index dc8d5c7..d0f7ab5 100644 --- a/tests/Blazored.FluentValidation.Tests/RuleSets/Tests.cs +++ b/tests/Blazored.FluentValidation.Tests/RuleSets/Tests.cs @@ -6,6 +6,21 @@ public class Tests : TestContext { private readonly Fixture _fixture = new(); + [Fact] + public void AddedByAttribute_PersonValid_ValidationPasses() + { + // Arrange + var person = _fixture.ValidPerson(); + var cut = RenderComponent(p => p.Add(c => c.IncludeWithCode, true)); + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Valid); + } + [Fact] public void AddedByAttribute_PersonFirstNameTooLong_ValidationFails() { @@ -15,15 +30,15 @@ public void AddedByAttribute_PersonFirstNameTooLong_ValidationFails() FirstName = "This name is clearly longer than 50 characters and thus should fail." }; var cut = RenderComponent(p => p.Add(c => c.IncludeWithCode, true)); - + // Act - cut.Find("input[name=FirstName]").Change(person.FirstName); + FillForm(cut, person); cut.Find("button").Click(); - + // Assert cut.Instance.Result.Should().Be(ValidationResultType.Error); } - + [Fact] public void AddedByAttribute_PersonFirstNameTooLong_ValidationMessagesPresent() { @@ -33,18 +48,32 @@ public void AddedByAttribute_PersonFirstNameTooLong_ValidationMessagesPresent() FirstName = "This name is clearly longer than 50 characters and thus should fail." }; var cut = RenderComponent(p => p.Add(c => c.IncludeWithCode, true)); - + // Act - cut.Find("input[name=FirstName]").Change(person.FirstName); + FillForm(cut, person); cut.Find("button").Click(); - + // Assert cut.Find(".validation-errors>.validation-message").TextContent.Should().Contain(PersonValidator.FirstNameMaxLength); cut.Find("li.validation-message").TextContent.Should().Contain(PersonValidator.FirstNameMaxLength); } + private static void FillForm(IRenderedComponent cut, Person person) + { + cut.Find($"input[name={nameof(Person.FirstName)}]").Change(person.FirstName); + cut.Find($"input[name={nameof(Person.LastName)}]").Change(person.LastName); + cut.Find($"input[name={nameof(Person.EmailAddress)}]").Change(person.EmailAddress); + cut.Find($"input[name={nameof(Person.Age)}]").Change(person.Age.ToString()); + } + private class Fixture { - public Person ValidPerson() => new() { FirstName = "John" }; + public Person ValidPerson() => new() + { + FirstName = "John", + LastName = "Doe", + EmailAddress = "john.doe@blazored.com", + Age = 30 + }; } } \ No newline at end of file From d472eaa9673feaa18042e2a2660dca9035ca4d91 Mon Sep 17 00:00:00 2001 From: luetm Date: Wed, 21 Feb 2024 16:27:14 +0100 Subject: [PATCH 08/11] Created readme for rule sets. Added tests for validation failure access. --- .../FullFailureAccess/AsyncComponent.razor | 55 +++++++++++++ .../FullFailureAccess/AsyncTests.cs | 77 +++++++++++++++++++ .../FullFailureAccess/Readme.md | 5 ++ .../FullFailureAccess/SyncComponent.razor | 54 +++++++++++++ .../FullFailureAccess/SyncTests.cs | 74 ++++++++++++++++++ .../Model/Person.cs | 6 ++ .../RuleSets/Readme.md | 17 ++++ 7 files changed, 288 insertions(+) create mode 100644 tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncComponent.razor create mode 100644 tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncTests.cs create mode 100644 tests/Blazored.FluentValidation.Tests/FullFailureAccess/Readme.md create mode 100644 tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncComponent.razor create mode 100644 tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncTests.cs create mode 100644 tests/Blazored.FluentValidation.Tests/RuleSets/Readme.md diff --git a/tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncComponent.razor b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncComponent.razor new file mode 100644 index 0000000..144144b --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncComponent.razor @@ -0,0 +1,55 @@ + + + + + +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ + +
+ +@code { + private readonly Person _person = new(); + private FluentValidationValidator? _fluentValidationValidator; + + public ValidationResultType? Result { get; private set; } + + protected async Task Submit() + { + await _fluentValidationValidator!.ValidateAsync(); + var lastResult = _fluentValidationValidator!.GetFailuresFromLastValidation(); + if (!lastResult.Any()) + { + Result = ValidationResultType.Valid; + } + else if (lastResult.Any(failure => failure.Severity == Severity.Error)) + { + Result = ValidationResultType.Error; + } + else + { + Result = ValidationResultType.Warning; + } + } + +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncTests.cs b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncTests.cs new file mode 100644 index 0000000..8491b24 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncTests.cs @@ -0,0 +1,77 @@ +using System; +using Blazored.FluentValidation.Tests.Model; + +namespace Blazored.FluentValidation.Tests.FullFailureAccess; + +public class AsyncTests : TestContext +{ + private readonly Fixture _fixture = new(); + + [Fact] + public void GetFailuresFromLastValidation_PersonValid_ResultIsValid() + { + // Arrange + var person = _fixture.ValidPerson(); + var cut = RenderComponent(); + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + cut.WaitForState(() => cut.Instance.Result is not null); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Valid); + } + + [Fact] + public void GetFailuresFromLastValidation_EmailInvalid_ResultIsError() + { + // Arrange + var person = _fixture.ValidPerson() with { EmailAddress = "invalid-email" }; + var cut = RenderComponent(); + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + cut.WaitForState(() => cut.Instance.Result is not null); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Error); + } + + [Fact] + public void GetFailuresFromLastValidation_AgeSuspect_ResultIsWarning() + { + // Arrange + var person = _fixture.ValidPerson() with { Age = 69 }; + var cut = RenderComponent(); + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + cut.WaitForState(() => cut.Instance.Result is not null); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Warning); + } + + + private static void FillForm(IRenderedComponent cut, Person person) + { + cut.Find($"input[name={nameof(Person.FirstName)}]").Change(person.FirstName); + cut.Find($"input[name={nameof(Person.LastName)}]").Change(person.LastName); + cut.Find($"input[name={nameof(Person.EmailAddress)}]").Change(person.EmailAddress); + cut.Find($"input[name={nameof(Person.Age)}]").Change(person.Age.ToString()); + } + + private class Fixture + { + public Person ValidPerson() => new() + { + FirstName = "John", + LastName = "Doe", + EmailAddress = "john.doe@blazored.com", + Age = 30 + }; + } +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/FullFailureAccess/Readme.md b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/Readme.md new file mode 100644 index 0000000..aff880a --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/Readme.md @@ -0,0 +1,5 @@ +### What does this test? +This test checks if the `GetFailuresFromLastValidation` method works correctly. It does so by both using `Validate` +and `ValidateAsync`. The failures given back are then checked for severity. + +To test warnings, the age of a person can be set to 69. diff --git a/tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncComponent.razor b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncComponent.razor new file mode 100644 index 0000000..4958393 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncComponent.razor @@ -0,0 +1,54 @@ + + + + + +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ + +
+ +@code { + private readonly Person _person = new(); + private FluentValidationValidator? _fluentValidationValidator; + + public ValidationResultType? Result { get; private set; } + + protected void Submit() + { + _fluentValidationValidator!.Validate(); + var lastResult = _fluentValidationValidator!.GetFailuresFromLastValidation(); + if (!lastResult.Any()) + { + Result = ValidationResultType.Valid; + } + else if (lastResult.Any(failure => failure.Severity == Severity.Error)) + { + Result = ValidationResultType.Error; + } + else + { + Result = ValidationResultType.Warning; + } + } +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncTests.cs b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncTests.cs new file mode 100644 index 0000000..78abfe9 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncTests.cs @@ -0,0 +1,74 @@ +using System; +using Blazored.FluentValidation.Tests.Model; + +namespace Blazored.FluentValidation.Tests.FullFailureAccess; + +public class SyncTests : TestContext +{ + private readonly Fixture _fixture = new(); + + [Fact] + public void GetFailuresFromLastValidation_PersonValid_ResultIsValid() + { + // Arrange + var person = _fixture.ValidPerson(); + var cut = RenderComponent(); + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Valid); + } + + [Fact] + public void GetFailuresFromLastValidation_EmailInvalid_ResultIsError() + { + // Arrange + var person = _fixture.ValidPerson() with { EmailAddress = "invalid-email" }; + var cut = RenderComponent(); + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Error); + } + + [Fact] + public void GetFailuresFromLastValidation_AgeSuspect_ResultIsWarning() + { + // Arrange + var person = _fixture.ValidPerson() with { Age = 69 }; + var cut = RenderComponent(); + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Warning); + } + + + private static void FillForm(IRenderedComponent cut, Person person) + { + cut.Find($"input[name={nameof(Person.FirstName)}]").Change(person.FirstName); + cut.Find($"input[name={nameof(Person.LastName)}]").Change(person.LastName); + cut.Find($"input[name={nameof(Person.EmailAddress)}]").Change(person.EmailAddress); + cut.Find($"input[name={nameof(Person.Age)}]").Change(person.Age.ToString()); + } + + private class Fixture + { + public Person ValidPerson() => new() + { + FirstName = "John", + LastName = "Doe", + EmailAddress = "john.doe@blazored.com", + Age = 30 + }; + } +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/Model/Person.cs b/tests/Blazored.FluentValidation.Tests/Model/Person.cs index 93a61a1..fc8c0f5 100644 --- a/tests/Blazored.FluentValidation.Tests/Model/Person.cs +++ b/tests/Blazored.FluentValidation.Tests/Model/Person.cs @@ -18,6 +18,7 @@ public class PersonValidator : AbstractValidator public const string AgeRequired = "You must enter your age"; public const string AgeMin = "Age must be greater than 0"; public const string AgeMax = "Age cannot be greater than 150"; + public const string AgeSuspect = "Age is suspect. Troll?"; public const string EmailRequired = "You must enter an email address"; public const string EmailValid = "You must provide a valid email address"; public const string EmailUnique = "Email address must be unique"; @@ -49,6 +50,11 @@ public PersonValidator() RuleFor(p => p.Address!) .SetValidator(new AddressValidator()) .When(p => p.Address is not null); + + RuleFor(p => p.Age) + .NotEqual(69) + .WithMessage(AgeSuspect) + .WithSeverity(Severity.Warning); } private static async Task IsUniqueAsync(string? email) diff --git a/tests/Blazored.FluentValidation.Tests/RuleSets/Readme.md b/tests/Blazored.FluentValidation.Tests/RuleSets/Readme.md new file mode 100644 index 0000000..afc393f --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/RuleSets/Readme.md @@ -0,0 +1,17 @@ +### What does this test? +This test checks if using the `IncludeRuleSets..` method work, once by attribute + +```html + +``` + +and once by code + +```csharp +@code { + private FluentValidationValidator? _fluentValidationValidator; + + private void PartialValidate() + => _fluentValidationValidator?.Validate(options => options.IncludeRuleSets("Names")); +} +``` \ No newline at end of file From 898e51b11eeee7b0cc2f2d086ed2b4b7d56b2d13 Mon Sep 17 00:00:00 2001 From: luetm Date: Wed, 21 Feb 2024 16:32:35 +0100 Subject: [PATCH 09/11] Tested `Validate` in addition to `ValidateAsync` --- .../AsyncComponent.razor} | 4 +- .../AsyncTests.cs} | 19 ++--- .../Readme.md | 0 .../DirectValidation/SyncComponent.razor | 39 ++++++++++ .../DirectValidation/SyncTests.cs | 73 +++++++++++++++++++ 5 files changed, 124 insertions(+), 11 deletions(-) rename tests/Blazored.FluentValidation.Tests/{AsyncValidation/Component.razor => DirectValidation/AsyncComponent.razor} (91%) rename tests/Blazored.FluentValidation.Tests/{AsyncValidation/Tests.cs => DirectValidation/AsyncTests.cs} (77%) rename tests/Blazored.FluentValidation.Tests/{AsyncValidation => DirectValidation}/Readme.md (100%) create mode 100644 tests/Blazored.FluentValidation.Tests/DirectValidation/SyncComponent.razor create mode 100644 tests/Blazored.FluentValidation.Tests/DirectValidation/SyncTests.cs diff --git a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor b/tests/Blazored.FluentValidation.Tests/DirectValidation/AsyncComponent.razor similarity index 91% rename from tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor rename to tests/Blazored.FluentValidation.Tests/DirectValidation/AsyncComponent.razor index 2acb1a0..c0cafe7 100644 --- a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Component.razor +++ b/tests/Blazored.FluentValidation.Tests/DirectValidation/AsyncComponent.razor @@ -1,4 +1,4 @@ - + @@ -31,7 +31,7 @@ public ValidationResultType? Result { get; private set; } - private async Task SubmitFormAsync() + private async Task SubmitAsync() { var result = await _fluentValidationValidator!.ValidateAsync(); Result = result ? ValidationResultType.Valid : ValidationResultType.Error; diff --git a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Tests.cs b/tests/Blazored.FluentValidation.Tests/DirectValidation/AsyncTests.cs similarity index 77% rename from tests/Blazored.FluentValidation.Tests/AsyncValidation/Tests.cs rename to tests/Blazored.FluentValidation.Tests/DirectValidation/AsyncTests.cs index 453cec4..634efe3 100644 --- a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Tests.cs +++ b/tests/Blazored.FluentValidation.Tests/DirectValidation/AsyncTests.cs @@ -1,15 +1,16 @@ using Blazored.FluentValidation.Tests.Model; -namespace Blazored.FluentValidation.Tests.AsyncValidation; -public class Tests : TestContext +namespace Blazored.FluentValidation.Tests.DirectValidation; + +public class AsyncTests : TestContext { private readonly Fixture _fixture = new(); [Fact] - public void AsyncValidate_PersonIsValid_ResultIsValid() + public void ValidateAsync_PersonIsValid_ResultIsValid() { // Arrange - var cut = RenderComponent(); + var cut = RenderComponent(); var person = _fixture.ValidPerson(); // Act @@ -22,10 +23,10 @@ public void AsyncValidate_PersonIsValid_ResultIsValid() } [Fact] - public void AsyncValidate_AgeNegative_ResultIsError() + public void ValidateAsync_AgeNegative_ResultIsError() { // Arrange - var cut = RenderComponent(); + var cut = RenderComponent(); var person = _fixture.ValidPerson() with { Age = -5 }; // Act @@ -38,10 +39,10 @@ public void AsyncValidate_AgeNegative_ResultIsError() } [Fact] - public void AsyncValidate_AgeNegative_ValidationMessagesPresent() + public void ValidateAsync_AgeNegative_ValidationMessagesPresent() { // Arrange - var cut = RenderComponent(); + var cut = RenderComponent(); var person = _fixture.ValidPerson() with { Age = -5 }; // Act @@ -54,7 +55,7 @@ public void AsyncValidate_AgeNegative_ValidationMessagesPresent() cut.Find("li.validation-message").TextContent.Should().Contain(PersonValidator.AgeMin); } - private void FillForm(IRenderedComponent cut, Person person) + private void FillForm(IRenderedComponent cut, Person person) { cut.Find("input[name=FirstName]").Change(person.FirstName); cut.Find("input[name=LastName]").Change(person.LastName); diff --git a/tests/Blazored.FluentValidation.Tests/AsyncValidation/Readme.md b/tests/Blazored.FluentValidation.Tests/DirectValidation/Readme.md similarity index 100% rename from tests/Blazored.FluentValidation.Tests/AsyncValidation/Readme.md rename to tests/Blazored.FluentValidation.Tests/DirectValidation/Readme.md diff --git a/tests/Blazored.FluentValidation.Tests/DirectValidation/SyncComponent.razor b/tests/Blazored.FluentValidation.Tests/DirectValidation/SyncComponent.razor new file mode 100644 index 0000000..00a73e9 --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/DirectValidation/SyncComponent.razor @@ -0,0 +1,39 @@ + + + + +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ + +
+ +@code { + private readonly Person _person = new(); + private FluentValidationValidator? _fluentValidationValidator; + + public ValidationResultType? Result { get; private set; } + + private void Submit() + { + var result = _fluentValidationValidator!.Validate(); + Result = result ? ValidationResultType.Valid : ValidationResultType.Error; + } +} \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/DirectValidation/SyncTests.cs b/tests/Blazored.FluentValidation.Tests/DirectValidation/SyncTests.cs new file mode 100644 index 0000000..cd48fcb --- /dev/null +++ b/tests/Blazored.FluentValidation.Tests/DirectValidation/SyncTests.cs @@ -0,0 +1,73 @@ +using Blazored.FluentValidation.Tests.Model; + +namespace Blazored.FluentValidation.Tests.DirectValidation; + +public class SyncTests : TestContext +{ + private readonly Fixture _fixture = new(); + + [Fact] + public void Validate_PersonIsValid_ResultIsValid() + { + // Arrange + var cut = RenderComponent(); + var person = _fixture.ValidPerson(); + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Valid); + } + + [Fact] + public void Validate_AgeNegative_ResultIsError() + { + // Arrange + var cut = RenderComponent(); + var person = _fixture.ValidPerson() with { Age = -5 }; + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Instance.Result.Should().Be(ValidationResultType.Error); + } + + [Fact] + public void Validate_AgeNegative_ValidationMessagesPresent() + { + // Arrange + var cut = RenderComponent(); + var person = _fixture.ValidPerson() with { Age = -5 }; + + // Act + FillForm(cut, person); + cut.Find("button").Click(); + + // Assert + cut.Find(".validation-errors>.validation-message").TextContent.Should().Contain(PersonValidator.AgeMin); + cut.Find("li.validation-message").TextContent.Should().Contain(PersonValidator.AgeMin); + } + + private void FillForm(IRenderedComponent cut, Person person) + { + cut.Find("input[name=FirstName]").Change(person.FirstName); + cut.Find("input[name=LastName]").Change(person.LastName); + cut.Find("input[name=Age]").Change(person.Age.ToString()); + cut.Find("input[name=EmailAddress]").Change(person.EmailAddress); + } + + private class Fixture + { + public Person ValidPerson() => new() + { + FirstName = "John", + LastName = "Doe", + Age = 30, + EmailAddress = "john.doe@blazored.com" + }; + } +} \ No newline at end of file From d0c91f3cf2c73eba2c31988f9caa13b3d8aa2a7c Mon Sep 17 00:00:00 2001 From: Paul Welter Date: Tue, 28 May 2024 14:39:30 -0500 Subject: [PATCH 10/11] add .net 8 support --- .github/workflows/ci-main.yml | 14 ++++++------- .github/workflows/ci-pr.yml | 2 +- .../Blazored.FluentValidation.csproj | 16 +++++++++----- .../EditContextFluentValidationExtensions.cs | 16 +++++++------- .../FluentValidationsValidator.cs | 14 ++++++------- .../AssemblyScanning/Component.razor | 8 +++---- .../AssemblyScanning/Tests.cs | 6 +++--- .../BasicValidation/Tests.cs | 10 ++++----- .../Blazored.FluentValidation.Tests.csproj | 18 ++++++++-------- .../FullFailureAccess/AsyncTests.cs | 18 ++++++++-------- .../FullFailureAccess/SyncTests.cs | 21 +++++++++---------- .../Model/Address.cs | 2 +- .../Model/Person.cs | 2 +- 13 files changed, 77 insertions(+), 70 deletions(-) diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index b513263..e2e24b6 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -7,7 +7,7 @@ on: types: [ published ] env: - NETCORE_VERSION: '7.0.x' + NETCORE_VERSION: '8.0.x' DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_CLI_TELEMETRY_OPTOUT: true PROJECT_NAME: FluentValidation @@ -42,12 +42,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - + - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: dotnet-version: ${{ env.NETCORE_VERSION }} - + - name: Create Release NuGet package run: | arrTag=(${GITHUB_REF//\// }) @@ -56,17 +56,17 @@ jobs: VERSION="${VERSION:1}" echo Clean Version: $VERSION dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg src/Blazored.$PROJECT_NAME/Blazored.$PROJECT_NAME.csproj - + - name: Push to NuGet Feed run: dotnet nuget push ./nupkg/*.nupkg --source $NUGET_FEED --api-key $NUGET_KEY --skip-duplicate - + - name: Publish Sample Site run: dotnet publish -c Release samples/BlazorWebAssembly/BlazorWebAssembly.csproj - name: Rewrite base href uses: SteveSandersonMS/ghaction-rewrite-base-href@v1 with: - html_path: samples/BlazorWebAssembly/bin/Release/net7.0/publish/wwwroot/index.html + html_path: samples/BlazorWebAssembly/bin/Release/net8.0/publish/wwwroot/index.html base_href: /${{ env.PROJECT_NAME }}/ - name: Deploy to Github Pages @@ -75,5 +75,5 @@ jobs: ACCESS_TOKEN: $GITHUB_TOKEN BASE_BRANCH: main # The branch the action should deploy from. BRANCH: gh-pages # The branch the action should deploy to. - FOLDER: samples/BlazorWebAssembly/bin/Release/net7.0/publish/wwwroot # The folder the action should deploy. + FOLDER: samples/BlazorWebAssembly/bin/Release/net8.0/publish/wwwroot # The folder the action should deploy. SINGLE_COMMIT: true diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index f3b4f98..864f0cf 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -6,7 +6,7 @@ on: env: PROJECT_NAME: Blazored.FluentValidation - NETCORE_VERSION: '7.0.x' + NETCORE_VERSION: '8.0.x' jobs: build: diff --git a/src/Blazored.FluentValidation/Blazored.FluentValidation.csproj b/src/Blazored.FluentValidation/Blazored.FluentValidation.csproj index 90ebca6..8add0c8 100644 --- a/src/Blazored.FluentValidation/Blazored.FluentValidation.csproj +++ b/src/Blazored.FluentValidation/Blazored.FluentValidation.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0 + net6.0;net7.0;net8.0 enable enable @@ -31,14 +31,20 @@ + + + + - - + - - + + + + + diff --git a/src/Blazored.FluentValidation/EditContextFluentValidationExtensions.cs b/src/Blazored.FluentValidation/EditContextFluentValidationExtensions.cs index 7ab8ab8..755a44b 100644 --- a/src/Blazored.FluentValidation/EditContextFluentValidationExtensions.cs +++ b/src/Blazored.FluentValidation/EditContextFluentValidationExtensions.cs @@ -1,8 +1,10 @@ using FluentValidation; using FluentValidation.Internal; using FluentValidation.Results; + using Microsoft.AspNetCore.Components.Forms; using Microsoft.Extensions.DependencyInjection; + using static FluentValidation.AssemblyScanner; namespace Blazored.FluentValidation; @@ -59,12 +61,12 @@ private static async Task ValidateModel(EditContext editContext, messages.Clear(); fluentValidationValidator.LastValidationResult = new Dictionary>(); - + foreach (var validationResult in validationResults.Errors) { var fieldIdentifier = ToFieldIdentifier(editContext, validationResult.PropertyName); messages.Add(fieldIdentifier, validationResult.ErrorMessage); - + if (fluentValidationValidator.LastValidationResult.TryGetValue(fieldIdentifier, out var failures)) { failures.Add(validationResult); @@ -88,7 +90,7 @@ private static async Task ValidateField(EditContext editContext, { var properties = new[] { fieldIdentifier.FieldName }; var context = new ValidationContext(fieldIdentifier.Model, new PropertyChain(), new MemberNameValidatorSelector(properties)); - + validator ??= GetValidatorForModel(serviceProvider, fieldIdentifier.Model, disableAssemblyScanning); if (validator is not null) @@ -160,7 +162,7 @@ private static FieldIdentifier ToFieldIdentifier(in EditContext editContext, in var obj = editContext.Model; var nextTokenEnd = propertyPath.IndexOfAny(Separators); - + // Optimize for a scenario when parsing isn't needed. if (nextTokenEnd < 0) { @@ -187,8 +189,8 @@ private static FieldIdentifier ToFieldIdentifier(in EditContext editContext, in // we've got an Item property var indexerType = prop.GetIndexParameters()[0].ParameterType; var indexerValue = Convert.ChangeType(nextToken.ToString(), indexerType); - - newObj = prop.GetValue(obj, new [] { indexerValue }); + + newObj = prop.GetValue(obj, new[] { indexerValue }); } else { @@ -233,7 +235,7 @@ private static FieldIdentifier ToFieldIdentifier(in EditContext editContext, in } obj = newObj; - + nextTokenEnd = propertyPathAsSpan.IndexOfAny(Separators); if (nextTokenEnd < 0) { diff --git a/src/Blazored.FluentValidation/FluentValidationsValidator.cs b/src/Blazored.FluentValidation/FluentValidationsValidator.cs index 0f8ee8a..c6a6a9e 100644 --- a/src/Blazored.FluentValidation/FluentValidationsValidator.cs +++ b/src/Blazored.FluentValidation/FluentValidationsValidator.cs @@ -1,9 +1,9 @@ using FluentValidation; using FluentValidation.Internal; +using FluentValidation.Results; + using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Forms; -using System; -using FluentValidation.Results; namespace Blazored.FluentValidation; @@ -48,7 +48,7 @@ public async Task ValidateAsync(Action>? option { throw new NullReferenceException(nameof(CurrentEditContext)); } - + ValidateOptions = options; try @@ -61,7 +61,7 @@ public async Task ValidateAsync(Action>? option throw new InvalidOperationException("No pending ValidationResult found"); } - await (Task) asyncValidationTask; + await (Task)asyncValidationTask; return !CurrentEditContext.GetValidationMessages().Any(); } @@ -95,10 +95,10 @@ public ValidationFailure[] GetFailuresFromLastValidation(FieldIdentifier? fieldI if (fieldIdentifier is null) return LastValidationResult.Values.SelectMany(f => f).ToArray(); - + if (!LastValidationResult.TryGetValue(fieldIdentifier.Value, out var failures)) - return Array.Empty(); - + return Array.Empty(); + return failures.ToArray(); } } \ No newline at end of file diff --git a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor index e501004..ca9d7a3 100644 --- a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor +++ b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Component.razor @@ -1,4 +1,4 @@ - @@ -11,21 +11,21 @@ { } - +

- +
@code { [Parameter] public bool? DisableAssemblyScanning { get; set; } private readonly Person _person = new(); - + internal ValidationResultType Result { get; private set; } = ValidationResultType.Valid; private void ValidSubmit() => Result = ValidationResultType.Valid; diff --git a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs index 32be18a..3144750 100644 --- a/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs +++ b/tests/Blazored.FluentValidation.Tests/AssemblyScanning/Tests.cs @@ -5,7 +5,7 @@ namespace Blazored.FluentValidation.Tests.AssemblyScanning; public class Tests : TestContext { private readonly Fixture _fixture = new(); - + [Fact] public void DisableAssemblyScanning_SetToTrue_NoValidationHappens() { @@ -50,7 +50,7 @@ public void DisableAssemblyScanning_NotSet_ValidationHappens() // Assert cut.Instance.Result.Should().Be(ValidationResultType.Error); } - + [Fact] public void DisableAssemblyScanning_SetToTrueButValidatorsRegistered_ValidationHappens() { @@ -66,7 +66,7 @@ public void DisableAssemblyScanning_SetToTrueButValidatorsRegistered_ValidationH // Assert cut.Instance.Result.Should().Be(ValidationResultType.Error); } - + private class Fixture { public Person InvalidPerson() => new() diff --git a/tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs b/tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs index fdcef88..69317f6 100644 --- a/tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs +++ b/tests/Blazored.FluentValidation.Tests/BasicValidation/Tests.cs @@ -58,26 +58,26 @@ public void Validate_AgeTooOld_ValidationErrorsPresent() // Arrange var cut = RenderComponent(); var person = _fixture.ValidPerson() with { Age = 250 }; - + // Act FillForm(cut, person); cut.Find("button").Click(); - + // Assert cut.Find(".validation-errors>.validation-message").TextContent.Should().Contain(PersonValidator.AgeMax); } - + [Fact] public void Validate_AddressLine1Missing_ValidationErrorsPresent() { // Arrange var cut = RenderComponent(); var person = _fixture.ValidPerson() with { Address = new() { Line1 = string.Empty } }; - + // Act FillForm(cut, person); cut.Find("button").Click(); - + // Assert cut.Find(".validation-errors>.validation-message").TextContent.Should().Contain(AddressValidator.Line1Required); } diff --git a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj index 8269342..495a7b2 100644 --- a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj +++ b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj @@ -1,7 +1,7 @@ - + - net7.0 + net8.0 enable false @@ -10,23 +10,23 @@ - + - + - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncTests.cs b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncTests.cs index 8491b24..aa6eb0a 100644 --- a/tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncTests.cs +++ b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/AsyncTests.cs @@ -13,48 +13,48 @@ public void GetFailuresFromLastValidation_PersonValid_ResultIsValid() // Arrange var person = _fixture.ValidPerson(); var cut = RenderComponent(); - + // Act FillForm(cut, person); cut.Find("button").Click(); cut.WaitForState(() => cut.Instance.Result is not null); - + // Assert cut.Instance.Result.Should().Be(ValidationResultType.Valid); } - + [Fact] public void GetFailuresFromLastValidation_EmailInvalid_ResultIsError() { // Arrange var person = _fixture.ValidPerson() with { EmailAddress = "invalid-email" }; var cut = RenderComponent(); - + // Act FillForm(cut, person); cut.Find("button").Click(); cut.WaitForState(() => cut.Instance.Result is not null); - + // Assert cut.Instance.Result.Should().Be(ValidationResultType.Error); } - + [Fact] public void GetFailuresFromLastValidation_AgeSuspect_ResultIsWarning() { // Arrange var person = _fixture.ValidPerson() with { Age = 69 }; var cut = RenderComponent(); - + // Act FillForm(cut, person); cut.Find("button").Click(); cut.WaitForState(() => cut.Instance.Result is not null); - + // Assert cut.Instance.Result.Should().Be(ValidationResultType.Warning); } - + private static void FillForm(IRenderedComponent cut, Person person) { diff --git a/tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncTests.cs b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncTests.cs index 78abfe9..107708b 100644 --- a/tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncTests.cs +++ b/tests/Blazored.FluentValidation.Tests/FullFailureAccess/SyncTests.cs @@ -1,5 +1,4 @@ -using System; -using Blazored.FluentValidation.Tests.Model; +using Blazored.FluentValidation.Tests.Model; namespace Blazored.FluentValidation.Tests.FullFailureAccess; @@ -13,45 +12,45 @@ public void GetFailuresFromLastValidation_PersonValid_ResultIsValid() // Arrange var person = _fixture.ValidPerson(); var cut = RenderComponent(); - + // Act FillForm(cut, person); cut.Find("button").Click(); - + // Assert cut.Instance.Result.Should().Be(ValidationResultType.Valid); } - + [Fact] public void GetFailuresFromLastValidation_EmailInvalid_ResultIsError() { // Arrange var person = _fixture.ValidPerson() with { EmailAddress = "invalid-email" }; var cut = RenderComponent(); - + // Act FillForm(cut, person); cut.Find("button").Click(); - + // Assert cut.Instance.Result.Should().Be(ValidationResultType.Error); } - + [Fact] public void GetFailuresFromLastValidation_AgeSuspect_ResultIsWarning() { // Arrange var person = _fixture.ValidPerson() with { Age = 69 }; var cut = RenderComponent(); - + // Act FillForm(cut, person); cut.Find("button").Click(); - + // Assert cut.Instance.Result.Should().Be(ValidationResultType.Warning); } - + private static void FillForm(IRenderedComponent cut, Person person) { diff --git a/tests/Blazored.FluentValidation.Tests/Model/Address.cs b/tests/Blazored.FluentValidation.Tests/Model/Address.cs index d7a5332..0eeebc2 100644 --- a/tests/Blazored.FluentValidation.Tests/Model/Address.cs +++ b/tests/Blazored.FluentValidation.Tests/Model/Address.cs @@ -15,7 +15,7 @@ public class AddressValidator : AbstractValidator
public const string TownRequired = "You must enter a town"; public const string CountyRequired = "You must enter a county"; public const string PostcodeRequired = "You must enter a postcode"; - + public AddressValidator() { RuleFor(p => p.Line1).NotEmpty().WithMessage(Line1Required); diff --git a/tests/Blazored.FluentValidation.Tests/Model/Person.cs b/tests/Blazored.FluentValidation.Tests/Model/Person.cs index fc8c0f5..0ab6ca6 100644 --- a/tests/Blazored.FluentValidation.Tests/Model/Person.cs +++ b/tests/Blazored.FluentValidation.Tests/Model/Person.cs @@ -23,7 +23,7 @@ public class PersonValidator : AbstractValidator public const string EmailValid = "You must provide a valid email address"; public const string EmailUnique = "Email address must be unique"; public const string DuplicateEmail = "mail@my.com"; - + public PersonValidator() { RuleSet("Names", () => From 9b9834a76446cfaf3a64937805bfe1603f212ea2 Mon Sep 17 00:00:00 2001 From: Jason Finch Date: Mon, 1 Jul 2024 08:25:55 +1000 Subject: [PATCH 11/11] -task (net8): Add net8 target to BlazorServer, SharedModels. Bump FluentValidation to 11.9.2 --- samples/BlazorServer/BlazorServer.csproj | 2 +- samples/BlazorWebAssembly/BlazorWebAssembly.csproj | 9 +++++++-- samples/Shared/SharedModels/SharedModels.csproj | 4 ++-- .../Blazored.FluentValidation.csproj | 2 +- .../Blazored.FluentValidation.Tests.csproj | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/samples/BlazorServer/BlazorServer.csproj b/samples/BlazorServer/BlazorServer.csproj index 4b3ab49..5056724 100644 --- a/samples/BlazorServer/BlazorServer.csproj +++ b/samples/BlazorServer/BlazorServer.csproj @@ -1,7 +1,7 @@  - net7.0 + net6.0;net7.0;net8.0 enable enable diff --git a/samples/BlazorWebAssembly/BlazorWebAssembly.csproj b/samples/BlazorWebAssembly/BlazorWebAssembly.csproj index 8ee79b3..7c8cf58 100644 --- a/samples/BlazorWebAssembly/BlazorWebAssembly.csproj +++ b/samples/BlazorWebAssembly/BlazorWebAssembly.csproj @@ -1,16 +1,21 @@  - net7.0 + net7.0;net8.0 enable enable - + + + + + + diff --git a/samples/Shared/SharedModels/SharedModels.csproj b/samples/Shared/SharedModels/SharedModels.csproj index 0337b08..7492bff 100644 --- a/samples/Shared/SharedModels/SharedModels.csproj +++ b/samples/Shared/SharedModels/SharedModels.csproj @@ -1,13 +1,13 @@  - net7.0 + net6.0;net7.0;net8.0 enable enable - + diff --git a/src/Blazored.FluentValidation/Blazored.FluentValidation.csproj b/src/Blazored.FluentValidation/Blazored.FluentValidation.csproj index 8add0c8..8d700f4 100644 --- a/src/Blazored.FluentValidation/Blazored.FluentValidation.csproj +++ b/src/Blazored.FluentValidation/Blazored.FluentValidation.csproj @@ -32,7 +32,7 @@ - + diff --git a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj index 495a7b2..c97d6b1 100644 --- a/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj +++ b/tests/Blazored.FluentValidation.Tests/Blazored.FluentValidation.Tests.csproj @@ -16,7 +16,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive