From f6b4ddf59896da12f781bb81016a0901d193ad9e Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Mon, 3 Mar 2025 13:13:50 +0100 Subject: [PATCH] Localize the email property editor validation and add tests (#18461) * Localize the email property editor validation and add tests. * Reverted trim to ensure behaviour for whitespace is unchanged. --- .../Validators/EmailValidator.cs | 33 +++++++++--- .../EmailAddressPropertyEditor.cs | 39 +++++++++++---- .../Validators/EmailValidatorTests.cs | 50 +++++++++++++++++++ 3 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/Validators/EmailValidatorTests.cs diff --git a/src/Umbraco.Core/PropertyEditors/Validators/EmailValidator.cs b/src/Umbraco.Core/PropertyEditors/Validators/EmailValidator.cs index 592b2dc2c7..e8866622b8 100644 --- a/src/Umbraco.Core/PropertyEditors/Validators/EmailValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/Validators/EmailValidator.cs @@ -1,24 +1,45 @@ using System.ComponentModel.DataAnnotations; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models.Validation; +using Umbraco.Cms.Core.Services; +using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors.Validators; /// -/// A validator that validates an email address +/// A validator that validates an email address. /// public sealed class EmailValidator : IValueValidator { + private readonly ILocalizedTextService _localizedTextService; + + /// + /// Initializes a new instance of the class. + /// + [Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")] + public EmailValidator() + : this(StaticServiceProvider.Instance.GetRequiredService()) + { + } + + /// + /// Initializes a new instance of the class. + /// + public EmailValidator(ILocalizedTextService localizedTextService) => _localizedTextService = localizedTextService; + /// public IEnumerable Validate(object? value, string? valueType, object? dataTypeConfiguration, PropertyValidationContext validationContext) { - var asString = value == null ? string.Empty : value.ToString(); + var valueAsString = value?.ToString() ?? string.Empty; - var emailVal = new EmailAddressAttribute(); + var emailAddressAttribute = new EmailAddressAttribute(); - if (asString != string.Empty && emailVal.IsValid(asString) == false) + if (valueAsString != string.Empty && emailAddressAttribute.IsValid(valueAsString) == false) { - // TODO: localize these! - yield return new ValidationResult("Email is invalid", new[] { "value" }); + yield return new ValidationResult( + _localizedTextService.Localize("validation", "invalidEmail", [valueAsString]), + ["value"]); } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs index cbd7ef3a8c..4a2c18b3b5 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs @@ -1,29 +1,50 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors.Validators; +using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Core.PropertyEditors; +/// +/// Defines an email address property editor. +/// [DataEditor( Constants.PropertyEditors.Aliases.EmailAddress, ValueEditorIsReusable = true)] public class EmailAddressPropertyEditor : DataEditor { - /// - /// The constructor will setup the property editor based on the attribute if one is found - /// - public EmailAddressPropertyEditor(IDataValueEditorFactory dataValueEditorFactory) - : base(dataValueEditorFactory) - => SupportsReadOnly = true; + private readonly ILocalizedTextService _localizedTextService; + /// + /// Initializes a new instance of the class. + /// + [Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")] + public EmailAddressPropertyEditor(IDataValueEditorFactory dataValueEditorFactory) + : this( + dataValueEditorFactory, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + /// + /// Initializes a new instance of the class. + /// + public EmailAddressPropertyEditor(IDataValueEditorFactory dataValueEditorFactory, ILocalizedTextService localizedTextService) + : base(dataValueEditorFactory) + { + SupportsReadOnly = true; + _localizedTextService = localizedTextService; + } + + /// protected override IDataValueEditor CreateValueEditor() { IDataValueEditor editor = base.CreateValueEditor(); - - // add an email address validator - editor.Validators.Add(new EmailValidator()); + editor.Validators.Add(new EmailValidator(_localizedTextService)); return editor; } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/Validators/EmailValidatorTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/Validators/EmailValidatorTests.cs new file mode 100644 index 0000000000..24d8bc67a7 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/Validators/EmailValidatorTests.cs @@ -0,0 +1,50 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Globalization; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Models.Validation; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.PropertyEditors.Validators; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors.Validators; + +[TestFixture] +public class EmailValidatorTests +{ + [TestCase(null, true)] + [TestCase("", true)] + [TestCase(" ", false)] + [TestCase("test@test.com", true)] + [TestCase("invalid", false)] + public void Validates_Email_Address(object? email, bool expectedSuccess) + { + var validator = CreateValidator(); + var result = validator.Validate(email, ValueTypes.String, null, PropertyValidationContext.Empty()); + if (expectedSuccess) + { + Assert.IsEmpty(result); + } + else + { + Assert.AreEqual(1, result.Count()); + + var validationResult = result.First(); + Assert.AreEqual("validation_invalidEmail", validationResult.ErrorMessage); + } + } + + private static EmailValidator CreateValidator() + { + var localizedTextServiceMock = new Mock(); + localizedTextServiceMock.Setup(x => x.Localize( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>())) + .Returns((string key, string alias, CultureInfo culture, IDictionary args) => $"{key}_{alias}"); + return new EmailValidator(localizedTextServiceMock.Object); + } +}