mirror of https://github.com/Squidex/squidex.git
51 changed files with 891 additions and 332 deletions
@ -0,0 +1,16 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Schemas |
|||
{ |
|||
public enum StringContentType |
|||
{ |
|||
Unspecified, |
|||
Html, |
|||
Markdown |
|||
} |
|||
} |
|||
@ -0,0 +1,118 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Infrastructure.Translations; |
|||
using Squidex.Text; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ValidateContent.Validators |
|||
{ |
|||
public sealed class StringTextValidator : IValidator |
|||
{ |
|||
private readonly Func<string, string>? transform; |
|||
private readonly int? minCharacters; |
|||
private readonly int? maxCharacters; |
|||
private readonly int? minWords; |
|||
private readonly int? maxWords; |
|||
|
|||
public StringTextValidator(Func<string, string>? transform = null, |
|||
int? minCharacters = null, |
|||
int? maxCharacters = null, |
|||
int? minWords = null, |
|||
int? maxWords = null) |
|||
{ |
|||
if (minCharacters > maxCharacters) |
|||
{ |
|||
throw new ArgumentException("Min characters must be greater than max characters.", nameof(minCharacters)); |
|||
} |
|||
|
|||
if (minWords > maxWords) |
|||
{ |
|||
throw new ArgumentException("Min words must be greater than max words.", nameof(minWords)); |
|||
} |
|||
|
|||
this.transform = transform; |
|||
this.minCharacters = minCharacters; |
|||
this.maxCharacters = maxCharacters; |
|||
this.minWords = minWords; |
|||
this.maxWords = maxWords; |
|||
} |
|||
|
|||
public Task ValidateAsync(object? value, ValidationContext context, AddError addError) |
|||
{ |
|||
if (value is string stringValue && !string.IsNullOrEmpty(stringValue)) |
|||
{ |
|||
if (transform != null) |
|||
{ |
|||
stringValue = transform(stringValue); |
|||
} |
|||
|
|||
if (minWords.HasValue || maxWords.HasValue) |
|||
{ |
|||
var words = stringValue.WordCount(); |
|||
|
|||
if (minWords.HasValue && maxWords.HasValue) |
|||
{ |
|||
if (minWords == maxWords && minWords != words) |
|||
{ |
|||
addError(context.Path, T.Get("contents.validation.wordCount", new { count = minWords })); |
|||
} |
|||
else if (words < minWords || words > maxWords) |
|||
{ |
|||
addError(context.Path, T.Get("contents.validation.wordsBetween", new { min = minWords, max = maxWords })); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (minWords.HasValue && words < minWords) |
|||
{ |
|||
addError(context.Path, T.Get("contents.validation.minWords", new { min = minWords })); |
|||
} |
|||
|
|||
if (maxWords.HasValue && words > maxWords) |
|||
{ |
|||
addError(context.Path, T.Get("contents.validation.maxWords", new { max = maxWords })); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (minCharacters.HasValue || maxCharacters.HasValue) |
|||
{ |
|||
var characters = stringValue.CharacterCount(); |
|||
|
|||
if (minCharacters.HasValue && maxCharacters.HasValue) |
|||
{ |
|||
if (minCharacters == maxCharacters && minCharacters != characters) |
|||
{ |
|||
addError(context.Path, T.Get("contents.validation.normalCharacterCount", new { count = minCharacters })); |
|||
} |
|||
else if (characters < minCharacters || characters > maxCharacters) |
|||
{ |
|||
addError(context.Path, T.Get("contents.validation.normalCharactersBetween", new { min = minCharacters, max = maxCharacters })); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (minCharacters.HasValue && characters < minCharacters) |
|||
{ |
|||
addError(context.Path, T.Get("contents.validation.minNormalCharacters", new { min = minCharacters })); |
|||
} |
|||
|
|||
if (maxCharacters.HasValue && characters > maxCharacters) |
|||
{ |
|||
addError(context.Path, T.Get("contents.validation.maxCharacters", new { max = maxCharacters })); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,199 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using FluentAssertions; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Core.TestHelpers; |
|||
using Squidex.Domain.Apps.Core.ValidateContent.Validators; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators |
|||
{ |
|||
public class StringTextValidatorTests : IClassFixture<TranslationsFixture> |
|||
{ |
|||
private readonly List<string> errors = new List<string>(); |
|||
|
|||
[Theory] |
|||
[InlineData(20, 10)] |
|||
public void Should_throw_error_if_min_characters_greater_than_max(int? min, int? max) |
|||
{ |
|||
Assert.Throws<ArgumentException>(() => new StringTextValidator(minCharacters: min, maxCharacters: max)); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(20, 10)] |
|||
public void Should_throw_error_if_min_words_greater_than_max(int? min, int? max) |
|||
{ |
|||
Assert.Throws<ArgumentException>(() => new StringTextValidator(minWords: min, maxWords: max)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_add_error_if_value_is_null() |
|||
{ |
|||
var sut = new StringTextValidator(minCharacters: 5); |
|||
|
|||
await sut.ValidateAsync(null, errors); |
|||
|
|||
Assert.Empty(errors); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_add_error_if_value_is_empty() |
|||
{ |
|||
var sut = new StringTextValidator(minCharacters: 5); |
|||
|
|||
await sut.ValidateAsync(string.Empty, errors); |
|||
|
|||
Assert.Empty(errors); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(null, null)] |
|||
[InlineData(1000, null)] |
|||
[InlineData(1000, 2000)] |
|||
[InlineData(null, 2000)] |
|||
public async Task Should_not_add_error_if_value_is_within_character_range(int? min, int? max) |
|||
{ |
|||
var sut = new StringTextValidator(minCharacters: min, maxCharacters: max); |
|||
|
|||
await sut.ValidateAsync(CreateString(1500), errors); |
|||
|
|||
Assert.Empty(errors); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_value_has_not_exact_number_of_characters() |
|||
{ |
|||
var sut = new StringTextValidator(minCharacters: 5, maxCharacters: 5); |
|||
|
|||
await sut.ValidateAsync(CreateString(4), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "Must have exactly 5 text character(s)." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_value_is_smaller_than_min_characters() |
|||
{ |
|||
var sut = new StringTextValidator(minCharacters: 2000); |
|||
|
|||
await sut.ValidateAsync(CreateString(1500), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "Must have at least 2000 text character(s)." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_value_is_greater_than_max_characters() |
|||
{ |
|||
var sut = new StringTextValidator(maxCharacters: 1000); |
|||
|
|||
await sut.ValidateAsync(CreateString(1500), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "Must not have more than 1000 text character(s)." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_collection_count_is_not_in_character_range() |
|||
{ |
|||
var sut = new StringTextValidator(minCharacters: 2000, maxCharacters: 5000); |
|||
|
|||
await sut.ValidateAsync(CreateString(1), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "Must have between 2000 and 5000 text character(s)." }); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(null, null, 1000)] |
|||
[InlineData(1000, null, 1000)] |
|||
[InlineData(1000, 2000, 1000)] |
|||
[InlineData(null, 2000, 1000)] |
|||
public async Task Should_not_add_error_if_value_is_within_word_range(int? min, int? max, int length) |
|||
{ |
|||
var sut = new StringTextValidator(minWords: min, maxWords: max); |
|||
|
|||
await sut.ValidateAsync(CreateSentence(length), errors); |
|||
|
|||
Assert.Empty(errors); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_value_has_not_exact_number_of_words() |
|||
{ |
|||
var sut = new StringTextValidator(minWords: 5, maxWords: 5); |
|||
|
|||
await sut.ValidateAsync(CreateSentence(4), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "Must have exactly 5 word(s)." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_value_is_smaller_than_min_words() |
|||
{ |
|||
var sut = new StringTextValidator(minWords: 2000); |
|||
|
|||
await sut.ValidateAsync(CreateSentence(1500), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "Must have at least 2000 word(s)." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_value_is_greater_than_max_words() |
|||
{ |
|||
var sut = new StringTextValidator(maxWords: 1000); |
|||
|
|||
await sut.ValidateAsync(CreateSentence(1500), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "Must not have more than 1000 word(s)." }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_error_if_collection_count_is_not_in_word_range() |
|||
{ |
|||
var sut = new StringTextValidator(minWords: 2000, maxWords: 5000); |
|||
|
|||
await sut.ValidateAsync(CreateSentence(1), errors); |
|||
|
|||
errors.Should().BeEquivalentTo( |
|||
new[] { "Must have between 2000 and 5000 word(s)." }); |
|||
} |
|||
|
|||
private static string CreateString(int size) |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
for (var i = 0; i < size; i++) |
|||
{ |
|||
sb.Append("x"); |
|||
} |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
private static string CreateSentence(int size) |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
for (var i = 0; i < size; i++) |
|||
{ |
|||
sb.Append("x"); |
|||
sb.Append(" "); |
|||
} |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
} |
|||
} |
|||
Binary file not shown.
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue