Browse Source

Validation improved.

pull/344/head
Sebastian Stehle 8 years ago
parent
commit
534a8998c2
  1. 30
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/CollectionValidator.cs
  2. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/PatternValidator.cs
  3. 26
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/RangeValidator.cs
  4. 26
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/StringLengthValidator.cs
  5. 4
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardApp.cs
  6. 14
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs
  7. 8
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs
  8. 6
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppLanguages.cs
  9. 14
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppPatterns.cs
  10. 8
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppRoles.cs
  11. 4
      src/Squidex.Domain.Apps.Entities/Assets/Guards/GuardAsset.cs
  12. 4
      src/Squidex.Domain.Apps.Entities/Comments/Guards/GuardComments.cs
  13. 4
      src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs
  14. 6
      src/Squidex.Domain.Apps.Entities/Rules/Guards/GuardRule.cs
  15. 4
      src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleTriggerValidator.cs
  16. 60
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs
  17. 6
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs
  18. 6
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs
  19. 4
      src/Squidex.Infrastructure/Guard.cs
  20. 100
      src/Squidex.Infrastructure/Not.cs
  21. 33
      src/Squidex/app/framework/angular/forms/control-errors.component.ts
  22. 160
      src/Squidex/app/framework/angular/forms/error-formatting.spec.ts
  23. 70
      src/Squidex/app/framework/angular/forms/error-formatting.ts
  24. 122
      src/Squidex/app/framework/angular/forms/validators.spec.ts
  25. 111
      src/Squidex/app/framework/angular/forms/validators.ts
  26. 12
      src/Squidex/app/shared/state/contents.forms.spec.ts
  27. 72
      src/Squidex/app/shared/state/contents.forms.ts
  28. 20
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ArrayFieldTests.cs
  29. 12
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs
  30. 4
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs
  31. 4
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/DateTimeFieldTests.cs
  32. 4
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs
  33. 12
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs
  34. 6
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs
  35. 12
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs
  36. 4
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/CollectionItemValidatorTests.cs
  37. 36
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/CollectionValidatorTests.cs
  38. 2
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/PatternValidatorTests.cs
  39. 16
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/RangeValidatorTests.cs
  40. 27
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/StringLengthValidatorTests.cs
  41. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppClientsTests.cs
  42. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs
  43. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppLanguagesTests.cs
  44. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppPatternsTests.cs
  45. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppTests.cs
  46. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/Guard/GuardContentTests.cs
  47. 12
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/ArrayFieldPropertiesTests.cs
  48. 40
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/AssetsFieldPropertiesTests.cs
  49. 6
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/DateTimeFieldPropertiesTests.cs
  50. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/NumberFieldPropertiesTests.cs
  51. 14
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/ReferencesFieldPropertiesTests.cs
  52. 14
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/StringFieldPropertiesTests.cs
  53. 14
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/TagsFieldPropertiesTests.cs
  54. 6
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaFieldTests.cs
  55. 6
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs

30
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/CollectionValidator.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections;
using System.Threading.Tasks;
using Squidex.Infrastructure.Tasks;
@ -19,6 +20,11 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
public CollectionValidator(bool isRequired, int? minItems = null, int? maxItems = null)
{
if (minItems.HasValue && maxItems.HasValue && minItems.Value > maxItems.Value)
{
throw new ArgumentException("Min length must be greater than max length.", nameof(minItems));
}
this.isRequired = isRequired;
this.minItems = minItems;
this.maxItems = maxItems;
@ -36,14 +42,28 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
return TaskHelper.Done;
}
if (minItems.HasValue && items.Count < minItems.Value)
if (minItems.HasValue && maxItems.HasValue)
{
addError(context.Path, $"Must have at least {minItems} item(s).");
if (minItems == maxItems && minItems != items.Count)
{
addError(context.Path, $"Must have exactly {maxItems} item(s).");
}
else if (items.Count < minItems || items.Count > maxItems)
{
addError(context.Path, $"Must have between {minItems} and {maxItems} item(s).");
}
}
if (maxItems.HasValue && items.Count > maxItems.Value)
else
{
addError(context.Path, $"Must have not more than {maxItems} item(s).");
if (minItems.HasValue && items.Count < minItems.Value)
{
addError(context.Path, $"Must have at least {minItems} item(s).");
}
if (maxItems.HasValue && items.Count > maxItems.Value)
{
addError(context.Path, $"Must not have more than {maxItems} item(s).");
}
}
return TaskHelper.Done;

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/PatternValidator.cs

@ -37,7 +37,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{
if (string.IsNullOrWhiteSpace(errorMessage))
{
addError(context.Path, "Not valid.");
addError(context.Path, "Does not match to the pattern.");
}
else
{

26
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/RangeValidator.cs

@ -18,7 +18,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
public RangeValidator(T? min, T? max)
{
if (min.HasValue && max.HasValue && min.Value.CompareTo(max.Value) >= 0)
if (min.HasValue && max.HasValue && min.Value.CompareTo(max.Value) > 0)
{
throw new ArgumentException("Min value must be greater than max value.", nameof(min));
}
@ -36,14 +36,28 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
var typedValue = (T)value;
if (min.HasValue && typedValue.CompareTo(min.Value) < 0)
if (min.HasValue && max.HasValue)
{
addError(context.Path, $"Must be greater than or equal to '{min}'.");
if (Equals(min, max) && Equals(min.Value, max.Value))
{
addError(context.Path, $"Must be exactly '{max}'.");
}
else if (typedValue.CompareTo(min.Value) < 0 || typedValue.CompareTo(max.Value) > 0)
{
addError(context.Path, $"Must be between '{min}' and '{max}'.");
}
}
if (max.HasValue && typedValue.CompareTo(max.Value) > 0)
else
{
addError(context.Path, $"Must be less than or equal to '{max}'.");
if (min.HasValue && typedValue.CompareTo(min.Value) < 0)
{
addError(context.Path, $"Must be greater or equal to '{min}'.");
}
if (max.HasValue && typedValue.CompareTo(max.Value) > 0)
{
addError(context.Path, $"Must be less or equal to '{max}'.");
}
}
return TaskHelper.Done;

26
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/StringLengthValidator.cs

@ -18,7 +18,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
public StringLengthValidator(int? minLength, int? maxLength)
{
if (minLength.HasValue && maxLength.HasValue && minLength.Value >= maxLength.Value)
if (minLength.HasValue && maxLength.HasValue && minLength.Value > maxLength.Value)
{
throw new ArgumentException("Min length must be greater than max length.", nameof(minLength));
}
@ -31,14 +31,28 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{
if (value is string stringValue && !string.IsNullOrEmpty(stringValue))
{
if (minLength.HasValue && stringValue.Length < minLength.Value)
if (minLength.HasValue && maxLength.HasValue)
{
addError(context.Path, $"Must have more than '{minLength}' characters.");
if (minLength == maxLength && minLength != stringValue.Length)
{
addError(context.Path, $"Must have exactly {maxLength} character(s).");
}
else if (stringValue.Length < minLength || stringValue.Length > maxLength)
{
addError(context.Path, $"Must have between {minLength} and {maxLength} character(s).");
}
}
if (maxLength.HasValue && stringValue.Length > maxLength.Value)
else
{
addError(context.Path, $"Must have less than '{maxLength}' characters.");
if (minLength.HasValue && stringValue.Length < minLength.Value)
{
addError(context.Path, $"Must have at least {minLength} character(s).");
}
if (maxLength.HasValue && stringValue.Length > maxLength.Value)
{
addError(context.Path, $"Must not have more than {maxLength} character(s).");
}
}
}

4
src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardApp.cs

@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (!command.Name.IsSlug())
{
e("Name must be a valid slug.", nameof(command.Name));
e(Not.ValidSlug("Name"), nameof(command.Name));
}
});
}
@ -36,7 +36,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (string.IsNullOrWhiteSpace(command.PlanId))
{
e("Plan id is required.", nameof(command.PlanId));
e(Not.Defined("Plan id"), nameof(command.PlanId));
return;
}

14
src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs

@ -21,7 +21,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (string.IsNullOrWhiteSpace(command.Id))
{
e("Client id is required.", nameof(command.Id));
e(Not.Defined("Client id"), nameof(command.Id));
}
else if (clients.ContainsKey(command.Id))
{
@ -40,7 +40,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (string.IsNullOrWhiteSpace(command.Id))
{
e("Client id is required.", nameof(command.Id));
e(Not.Defined("Client id"), nameof(command.Id));
}
});
}
@ -55,17 +55,17 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (string.IsNullOrWhiteSpace(command.Id))
{
e("Client id is required.", nameof(command.Id));
e(Not.Defined("Client id"), nameof(command.Id));
}
if (string.IsNullOrWhiteSpace(command.Name) && command.Role == null)
{
e("Either name or role must be defined.", nameof(command.Name), nameof(command.Role));
e(Not.DefinedOr("name", "role"), nameof(command.Name), nameof(command.Role));
}
if (command.Role != null && !roles.ContainsKey(command.Role))
{
e("Role is not valid.", nameof(command.Role));
e(Not.Valid("role"), nameof(command.Role));
}
if (client == null)
@ -75,12 +75,12 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
if (!string.IsNullOrWhiteSpace(command.Name) && string.Equals(client.Name, command.Name))
{
e("Client has already this name.", nameof(command.Name));
e(Not.New("Client", "name"), nameof(command.Name));
}
if (command.Role == client.Role)
{
e("Client has already this role.", nameof(command.Role));
e(Not.New("Client", "role"), nameof(command.Role));
}
});
}

8
src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs

@ -26,12 +26,12 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (!roles.ContainsKey(command.Role))
{
e("Role is not valid.", nameof(command.Role));
e(Not.Valid("role"), nameof(command.Role));
}
if (string.IsNullOrWhiteSpace(command.ContributorId))
{
e("Contributor id is required.", nameof(command.ContributorId));
e(Not.Defined("Contributor id"), nameof(command.ContributorId));
return;
}
@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (existing == command.Role)
{
e("Contributor has already this role.", nameof(command.Role));
e(Not.New("Contributor", "role"), nameof(command.Role));
}
}
else if (plan.MaxContributors == contributors.Count)
@ -71,7 +71,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (string.IsNullOrWhiteSpace(command.ContributorId))
{
e("Contributor id is required.", nameof(command.ContributorId));
e(Not.Defined("Contributor id"), nameof(command.ContributorId));
}
var ownerIds = contributors.Where(x => x.Value == Role.Owner).Select(x => x.Key).ToList();

6
src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppLanguages.cs

@ -21,7 +21,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (command.Language == null)
{
e("Language code is required.", nameof(command.Language));
e(Not.Defined("Language code"), nameof(command.Language));
}
else if (languages.Contains(command.Language))
{
@ -40,7 +40,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (command.Language == null)
{
e("Language code is required.", nameof(command.Language));
e(Not.Defined("Language code"), nameof(command.Language));
}
if (languages.Master == config)
@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (command.Language == null)
{
e("Language is required.", nameof(command.Language));
e(Not.Defined("Language code"), nameof(command.Language));
}
if ((languages.Master == config || command.IsMaster) && command.IsOptional)

14
src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppPatterns.cs

@ -22,12 +22,12 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (command.PatternId == Guid.Empty)
{
e("Id is required.", nameof(command.PatternId));
e(Not.Defined("Id"), nameof(command.PatternId));
}
if (string.IsNullOrWhiteSpace(command.Name))
{
e("Name is required.", nameof(command.Name));
e(Not.Defined("Name"), nameof(command.Name));
}
if (patterns.Values.Any(x => x.Name.Equals(command.Name, StringComparison.OrdinalIgnoreCase)))
@ -37,11 +37,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
if (string.IsNullOrWhiteSpace(command.Pattern))
{
e("Pattern is required.", nameof(command.Pattern));
e(Not.Defined("Pattern"), nameof(command.Pattern));
}
else if (!command.Pattern.IsValidRegex())
{
e("Pattern is not a valid regular expression.", nameof(command.Pattern));
e(Not.Valid("Pattern"), nameof(command.Pattern));
}
if (patterns.Values.Any(x => x.Pattern == command.Pattern))
@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (string.IsNullOrWhiteSpace(command.Name))
{
e("Name is required.", nameof(command.Name));
e(Not.Defined("Name"), nameof(command.Name));
}
if (patterns.Any(x => x.Key != command.PatternId && x.Value.Name.Equals(command.Name, StringComparison.OrdinalIgnoreCase)))
@ -84,11 +84,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
if (string.IsNullOrWhiteSpace(command.Pattern))
{
e("Pattern is required.", nameof(command.Pattern));
e(Not.Defined("Pattern"), nameof(command.Pattern));
}
else if (!command.Pattern.IsValidRegex())
{
e("Pattern is not a valid regular expression.", nameof(command.Pattern));
e(Not.Valid("Pattern"), nameof(command.Pattern));
}
if (patterns.Any(x => x.Key != command.PatternId && x.Value.Pattern == command.Pattern))

8
src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppRoles.cs

@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (string.IsNullOrWhiteSpace(command.Name))
{
e("Name is required.", nameof(command.Name));
e(Not.Defined("Name"), nameof(command.Name));
}
else if (roles.ContainsKey(command.Name))
{
@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (string.IsNullOrWhiteSpace(command.Name))
{
e("Name is required.", nameof(command.Name));
e(Not.Defined("Name"), nameof(command.Name));
}
else if (Role.IsDefaultRole(command.Name))
{
@ -71,7 +71,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
if (string.IsNullOrWhiteSpace(command.Name))
{
e("Name is required.", nameof(command.Name));
e(Not.Defined("Name"), nameof(command.Name));
}
else if (Role.IsDefaultRole(command.Name))
{
@ -80,7 +80,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
if (command.Permissions == null)
{
e("Permissions is required.", nameof(command.Permissions));
e(Not.Defined("Permissions"), nameof(command.Permissions));
}
});
}

4
src/Squidex.Domain.Apps.Entities/Assets/Guards/GuardAsset.cs

@ -20,12 +20,12 @@ namespace Squidex.Domain.Apps.Entities.Assets.Guards
{
if (string.IsNullOrWhiteSpace(command.FileName))
{
e("Name is required.", nameof(command.FileName));
e(Not.Defined("Name"), nameof(command.FileName));
}
if (string.Equals(command.FileName, oldName))
{
e("Asset has already this name.", nameof(command.FileName));
e(Not.New("Asset", "name"), nameof(command.FileName));
}
});
}

4
src/Squidex.Domain.Apps.Entities/Comments/Guards/GuardComments.cs

@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.Guards
{
if (string.IsNullOrWhiteSpace(command.Text))
{
e("Text is required.", nameof(command.Text));
e(Not.Defined("Text"), nameof(command.Text));
}
});
}
@ -44,7 +44,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.Guards
{
if (string.IsNullOrWhiteSpace(command.Text))
{
e("Text is required.", nameof(command.Text));
e(Not.Defined("Text"), nameof(command.Text));
}
});
}

4
src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs

@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards
{
if (!StatusFlow.Exists(command.Status))
{
e("Status is not valid.", nameof(command.Status));
e(Not.Valid("Status"), nameof(command.Status));
}
else if (!StatusFlow.CanChange(status, command.Status))
{
@ -111,7 +111,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards
{
if (command.Data == null)
{
e("Data is required.", nameof(command.Data));
e(Not.Defined("Data"), nameof(command.Data));
}
}
}

6
src/Squidex.Domain.Apps.Entities/Rules/Guards/GuardRule.cs

@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
{
if (command.Trigger == null)
{
e("Trigger is required.", nameof(command.Trigger));
e(Not.Defined("Trigger"), nameof(command.Trigger));
}
else
{
@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
if (command.Action == null)
{
e("Action is required.", nameof(command.Action));
e(Not.Defined("Action"), nameof(command.Action));
}
else
{
@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
{
if (command.Trigger == null && command.Action == null)
{
e("Either trigger or action is required.", nameof(command.Trigger), nameof(command.Action));
e(Not.Defined("Either trigger or action"), nameof(command.Trigger), nameof(command.Action));
}
if (command.Trigger != null)

4
src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleTriggerValidator.cs

@ -46,7 +46,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
if (trigger.NumDays.HasValue && (trigger.NumDays < 1 || trigger.NumDays > 30))
{
errors.Add(new ValidationError("Num days must be between 1 and 30.", nameof(trigger.NumDays)));
errors.Add(new ValidationError(Not.Between("Num days", 1, 30), nameof(trigger.NumDays)));
}
return Task.FromResult< IEnumerable<ValidationError>>(errors);
@ -64,7 +64,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
{
if (schema.SchemaId == Guid.Empty)
{
errors.Add(new ValidationError("Schema id is required.", nameof(trigger.Schemas)));
errors.Add(new ValidationError(Not.Defined("Schema id"), nameof(trigger.Schemas)));
}
else
{

60
src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs

@ -26,9 +26,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
public IEnumerable<ValidationError> Visit(ArrayFieldProperties properties)
{
if (properties.MaxItems.HasValue && properties.MinItems.HasValue && properties.MinItems.Value >= properties.MaxItems.Value)
if (properties.MaxItems.HasValue && properties.MinItems.HasValue && properties.MinItems.Value > properties.MaxItems.Value)
{
yield return new ValidationError("Max items must be greater than min items.",
yield return new ValidationError(Not.GreaterEquals("Max items", "min items"),
nameof(properties.MinItems),
nameof(properties.MaxItems));
}
@ -36,37 +36,37 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
public IEnumerable<ValidationError> Visit(AssetsFieldProperties properties)
{
if (properties.MaxItems.HasValue && properties.MinItems.HasValue && properties.MinItems.Value >= properties.MaxItems.Value)
if (properties.MaxItems.HasValue && properties.MinItems.HasValue && properties.MinItems.Value > properties.MaxItems.Value)
{
yield return new ValidationError("Max items must be greater than min items.",
yield return new ValidationError(Not.GreaterEquals("Max items", "min items"),
nameof(properties.MinItems),
nameof(properties.MaxItems));
}
if (properties.MaxHeight.HasValue && properties.MinHeight.HasValue && properties.MinHeight.Value >= properties.MaxHeight.Value)
if (properties.MaxHeight.HasValue && properties.MinHeight.HasValue && properties.MinHeight.Value > properties.MaxHeight.Value)
{
yield return new ValidationError("Max height must be greater than min height.",
yield return new ValidationError(Not.GreaterEquals("Max height", "min height"),
nameof(properties.MaxHeight),
nameof(properties.MinHeight));
}
if (properties.MaxWidth.HasValue && properties.MinWidth.HasValue && properties.MinWidth.Value >= properties.MaxWidth.Value)
if (properties.MaxWidth.HasValue && properties.MinWidth.HasValue && properties.MinWidth.Value > properties.MaxWidth.Value)
{
yield return new ValidationError("Max width must be greater than min width.",
yield return new ValidationError(Not.GreaterEquals("Max width", "min width"),
nameof(properties.MaxWidth),
nameof(properties.MinWidth));
}
if (properties.MaxSize.HasValue && properties.MinSize.HasValue && properties.MinSize.Value >= properties.MaxSize.Value)
{
yield return new ValidationError("Max size must be greater than min size.",
yield return new ValidationError(Not.GreaterThan("Max size", "min size"),
nameof(properties.MaxSize),
nameof(properties.MinSize));
}
if (properties.AspectWidth.HasValue != properties.AspectHeight.HasValue)
{
yield return new ValidationError("Aspect width and height is required.",
yield return new ValidationError(Not.Defined2("Aspect width", "aspect height"),
nameof(properties.AspectWidth),
nameof(properties.AspectHeight));
}
@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
if (!properties.Editor.IsEnumValue())
{
yield return new ValidationError("Editor is not a valid value.",
yield return new ValidationError(Not.Valid("Editor"),
nameof(properties.Editor));
}
}
@ -85,25 +85,25 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
if (!properties.Editor.IsEnumValue())
{
yield return new ValidationError("Editor is not a valid value.",
yield return new ValidationError(Not.Valid("Editor"),
nameof(properties.Editor));
}
if (properties.DefaultValue.HasValue && properties.MinValue.HasValue && properties.DefaultValue.Value < properties.MinValue.Value)
{
yield return new ValidationError("Default value must be greater than min value.",
yield return new ValidationError(Not.GreaterEquals("Default value", "min value"),
nameof(properties.DefaultValue));
}
if (properties.DefaultValue.HasValue && properties.MaxValue.HasValue && properties.DefaultValue.Value > properties.MaxValue.Value)
{
yield return new ValidationError("Default value must be less than max value.",
yield return new ValidationError(Not.LessEquals("Default value", "max value"),
nameof(properties.DefaultValue));
}
if (properties.MaxValue.HasValue && properties.MinValue.HasValue && properties.MinValue.Value >= properties.MaxValue.Value)
{
yield return new ValidationError("Max value must be greater than min value.",
yield return new ValidationError(Not.GreaterThan("Max value", "min value"),
nameof(properties.MinValue),
nameof(properties.MaxValue));
}
@ -112,7 +112,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
if (!properties.CalculatedDefaultValue.Value.IsEnumValue())
{
yield return new ValidationError("Calculated default value is not valid.",
yield return new ValidationError(Not.Valid("Calculated default value"),
nameof(properties.CalculatedDefaultValue));
}
@ -129,7 +129,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
if (!properties.Editor.IsEnumValue())
{
yield return new ValidationError("Editor is not a valid value.",
yield return new ValidationError(Not.Valid("Editor"),
nameof(properties.Editor));
}
}
@ -143,7 +143,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
if (!properties.Editor.IsEnumValue())
{
yield return new ValidationError("Editor is not a valid value.",
yield return new ValidationError(Not.Valid("Editor"),
nameof(properties.Editor));
}
@ -155,19 +155,19 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
if (properties.DefaultValue.HasValue && properties.MinValue.HasValue && properties.DefaultValue.Value < properties.MinValue.Value)
{
yield return new ValidationError("Default value must be greater than min value.",
yield return new ValidationError(Not.GreaterEquals("Default value", "min value"),
nameof(properties.DefaultValue));
}
if (properties.DefaultValue.HasValue && properties.MaxValue.HasValue && properties.DefaultValue.Value > properties.MaxValue.Value)
{
yield return new ValidationError("Default value must be less than max value.",
yield return new ValidationError(Not.LessEquals("Default value", "max value"),
nameof(properties.DefaultValue));
}
if (properties.MaxValue.HasValue && properties.MinValue.HasValue && properties.MinValue.Value >= properties.MaxValue.Value)
{
yield return new ValidationError("Max value must be greater than min value.",
yield return new ValidationError(Not.GreaterThan("Max value", "min value"),
nameof(properties.MinValue),
nameof(properties.MaxValue));
}
@ -190,9 +190,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
public IEnumerable<ValidationError> Visit(ReferencesFieldProperties properties)
{
if (properties.MaxItems.HasValue && properties.MinItems.HasValue && properties.MinItems.Value >= properties.MaxItems.Value)
if (properties.MaxItems.HasValue && properties.MinItems.HasValue && properties.MinItems.Value > properties.MaxItems.Value)
{
yield return new ValidationError("Max items must be greater than min items.",
yield return new ValidationError(Not.GreaterEquals("Max items", "min items"),
nameof(properties.MinItems),
nameof(properties.MaxItems));
}
@ -202,7 +202,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
if (!properties.Editor.IsEnumValue())
{
yield return new ValidationError("Editor is not a valid value.",
yield return new ValidationError(Not.Valid("Editor"),
nameof(properties.Editor));
}
@ -214,13 +214,13 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
if (properties.Pattern != null && !properties.Pattern.IsValidRegex())
{
yield return new ValidationError("Pattern is not a valid expression.",
yield return new ValidationError(Not.Valid("Pattern"),
nameof(properties.Pattern));
}
if (properties.MaxLength.HasValue && properties.MinLength.HasValue && properties.MinLength.Value >= properties.MaxLength.Value)
if (properties.MaxLength.HasValue && properties.MinLength.HasValue && properties.MinLength.Value > properties.MaxLength.Value)
{
yield return new ValidationError("Max length must be greater than min length.",
yield return new ValidationError(Not.GreaterEquals("Max length", "min length"),
nameof(properties.MinLength),
nameof(properties.MaxLength));
}
@ -245,7 +245,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
if (!properties.Editor.IsEnumValue())
{
yield return new ValidationError("Editor is not a valid value.",
yield return new ValidationError(Not.Valid("Editor"),
nameof(properties.Editor));
}
@ -255,9 +255,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
nameof(properties.AllowedValues));
}
if (properties.MaxItems.HasValue && properties.MinItems.HasValue && properties.MinItems.Value >= properties.MaxItems.Value)
if (properties.MaxItems.HasValue && properties.MinItems.HasValue && properties.MinItems.Value > properties.MaxItems.Value)
{
yield return new ValidationError("Max items must be greater than min items.",
yield return new ValidationError(Not.GreaterEquals("Max items", "min items"),
nameof(properties.MinItems),
nameof(properties.MaxItems));
}

6
src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs

@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
if (!command.Name.IsSlug())
{
e("Name is not a valid slug.", nameof(command.Name));
e(Not.ValidSlug("Name"), nameof(command.Name));
}
else if (await appProvider.GetSchemaAsync(command.AppId.Id, command.Name) != null)
{
@ -44,7 +44,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
if (!field.Partitioning.IsValidPartitioning())
{
e("Field partitioning is not valid.", $"{fieldPrefix}.{nameof(field.Partitioning)}");
e(Not.Valid("Partitioning"), $"{fieldPrefix}.{nameof(field.Partitioning)}");
}
ValidateField(e, fieldPrefix, field);
@ -180,7 +180,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
if (field.Properties == null)
{
e("Field properties is required.", $"{prefix}.{nameof(field.Properties)}");
e(Not.Defined("Field properties"), $"{prefix}.{nameof(field.Properties)}");
}
else
{

6
src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs

@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
if (command.Properties == null)
{
e("Properties is required.", nameof(command.Properties));
e(Not.Defined("Properties"), nameof(command.Properties));
}
else
{
@ -49,7 +49,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
if (command.ParentFieldId == null && !command.Partitioning.IsValidPartitioning())
{
e("Partitioning is not valid.", nameof(command.Partitioning));
e(Not.Valid("Partitioning"), nameof(command.Partitioning));
}
if (schema.FieldsByName.ContainsKey(command.Name))
@ -75,7 +75,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
if (command.Properties == null)
{
e("Properties is required.", nameof(command.Properties));
e(Not.Defined("Properties"), nameof(command.Properties));
}
else
{

4
src/Squidex.Infrastructure/Guard.cs

@ -116,7 +116,7 @@ namespace Squidex.Infrastructure
{
if (target.CompareTo(lower) < 0)
{
throw new ArgumentException($"Value must be greater than or equal to {lower}", parameterName);
throw new ArgumentException($"Value must be greater or equal to {lower}", parameterName);
}
}
@ -136,7 +136,7 @@ namespace Squidex.Infrastructure
{
if (target.CompareTo(upper) > 0)
{
throw new ArgumentException($"Value must be less than or equal to {upper}", parameterName);
throw new ArgumentException($"Value must be less or equal to {upper}", parameterName);
}
}

100
src/Squidex.Infrastructure/Not.cs

@ -0,0 +1,100 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Runtime.CompilerServices;
namespace Squidex.Infrastructure
{
public static class Not
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Defined(string property)
{
return $"{Upper(property)} is required.";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Defined2(string property1, string property2)
{
return $"If {Lower(property1)} or {Lower(property2)} is used both must be defined.";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string ValidSlug(string property)
{
return $"{Upper(property)} is not a valid slug.";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string GreaterThan(string property, string other)
{
return $"{Upper(property)} must be greater than {Lower(other)}.";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string GreaterEquals(string property, string other)
{
return $"{Upper(property)} must be greater or equal to {Lower(other)}.";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string LessThan(string property, string other)
{
return $"{Upper(property)} must be less than {Lower(other)}.";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string LessEquals(string property, string other)
{
return $"{Upper(property)} must be less or equal to {Lower(other)}.";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Between<T>(string property, T min, T max)
{
return $"{Upper(property)} must be between {min} and {max}.";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Valid(string property)
{
return $"{Upper(property)} is not a valid value.";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string New(string type, string property)
{
return $"{Upper(type)} has already this {Lower(property)}.";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string DefinedOr(string property1, string property2)
{
return $"Either {Lower(property1)} or {Lower(property2)} must be defined.";
}
private static string Lower(string property)
{
if (char.IsUpper(property[0]))
{
return char.ToLower(property[0]) + property.Substring(1);
}
return property;
}
private static string Upper(string property)
{
if (char.IsLower(property[0]))
{
return char.ToUpper(property[0]) + property.Substring(1);
}
return property;
}
}
}

33
src/Squidex/app/framework/angular/forms/control-errors.component.ts

@ -11,20 +11,7 @@ import { merge, Subscription } from 'rxjs';
import { fadeAnimation, Types } from '@app/framework/internal';
const DEFAULT_ERRORS: { [key: string]: string } = {
required: '{field} is required.',
pattern: '{field} does not follow the pattern.',
patternmessage: '{message}',
minvalue: '{field} must be larger or equals to {minValue}.',
maxvalue: '{field} must be smaller or equals to {maxValue}.',
minmax: '{field} must have a length of more than {requiredLength}.',
maxlength: '{field} must have a length of less than {requiredLength}.',
match: '{message}',
validdatetime: '{field} is not a valid date time',
validnumber: '{field} is not a valid number.',
validvalues: '{field} is not a valid value.',
validarrayvalues: '{field} contains an invalid value.'
};
import { formatError } from './error-formatting';
@Component({
selector: 'sqx-control-errors',
@ -130,23 +117,11 @@ export class ControlErrorsComponent implements OnChanges, OnDestroy {
if (this.control && this.control.invalid && ((this.control.touched && !this.submitOnly) || this.submitted) && this.control.errors) {
for (let key in <any>this.control.errors) {
if (this.control.errors.hasOwnProperty(key)) {
let message = (this.errors ? this.errors[key] : null) || DEFAULT_ERRORS[key.toLowerCase()];
const message = formatError(this.displayFieldName, key, this.control.errors[key], this.control.value, this.errors);
if (!message) {
continue;
if (message) {
errors.push(message);
}
const properties = this.control.errors[key];
for (let property in properties) {
if (properties.hasOwnProperty(property)) {
message = message.replace(`{${property}}`, properties[property]);
}
}
message = message.replace('{field}', this.displayFieldName);
errors.push(message);
}
}
}

160
src/Squidex/app/framework/angular/forms/error-formatting.spec.ts

@ -0,0 +1,160 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
import { ValidatorsEx } from './validators';
import { formatError } from './error-formatting';
describe('formatErrors', () => {
it('should format min', () => {
const error = validate(1, Validators.min(2));
expect(error).toEqual('MY_FIELD must be greater or equal to \'2\'.');
});
it('should format max', () => {
const error = validate(3, Validators.max(2));
expect(error).toEqual('MY_FIELD must be less or equal to \'2\'.');
});
it('should format required', () => {
const error = validate(undefined, Validators.required);
expect(error).toEqual('MY_FIELD is required.');
});
it('should format requiredTrue', () => {
const error = validate(undefined, Validators.requiredTrue);
expect(error).toEqual('MY_FIELD is required.');
});
it('should format email', () => {
const error = validate('invalid', Validators.email);
expect(error).toEqual('MY_FIELD must be an email address.');
});
it('should format minLength string', () => {
const error = validate('x', Validators.minLength(2));
expect(error).toEqual('MY_FIELD must have at least 2 character(s).');
});
it('should format maxLength string', () => {
const error = validate('xxx', Validators.maxLength(2));
expect(error).toEqual('MY_FIELD must not have more than 2 character(s).');
});
it('should format minLength array', () => {
const error = validate([1], Validators.minLength(2));
expect(error).toEqual('MY_FIELD must have at least 2 item(s).');
});
it('should format maxLength array', () => {
const error = validate([1, 1, 1], Validators.maxLength(2));
expect(error).toEqual('MY_FIELD must not have more than 2 item(s).');
});
it('should format match', () => {
const error = validate('123', Validators.pattern('[A-Z]'));
expect(error).toEqual('MY_FIELD does not match to the pattern.');
});
it('should format match with message', () => {
const error = validate('123', ValidatorsEx.pattern('[A-Z]', 'Custom Message'));
expect(error).toEqual('Custom Message');
});
it('should format between exactly', () => {
const error = validate(2, ValidatorsEx.between(3, 3));
expect(error).toEqual('MY_FIELD must be exactly \'3\'.');
});
it('should format between range', () => {
const error = validate(2, ValidatorsEx.between(3, 5));
expect(error).toEqual('MY_FIELD must be between \'3\' and \'5\'.');
});
it('should format betweenLength string exactly', () => {
const error = validate('xx', ValidatorsEx.betweenLength(3, 3));
expect(error).toEqual('MY_FIELD must have exactly 3 character(s).');
});
it('should format betweenLength string range', () => {
const error = validate('xx', ValidatorsEx.betweenLength(3, 5));
expect(error).toEqual('MY_FIELD must have between 3 and 5 character(s).');
});
it('should format betweenLength array exactly', () => {
const error = validate([1], ValidatorsEx.betweenLength(3, 3));
expect(error).toEqual('MY_FIELD must have exactly 3 item(s).');
});
it('should format betweenLength array range', () => {
const error = validate([1, 1], ValidatorsEx.betweenLength(3, 5));
expect(error).toEqual('MY_FIELD must have between 3 and 5 item(s).');
});
it('should format validDateTime', () => {
const error = validate('invalid', ValidatorsEx.validDateTime());
expect(error).toEqual('MY_FIELD is not a valid date time.');
});
it('should format validValues', () => {
const error = validate(5, ValidatorsEx.validValues([1, 2, 3]));
expect(error).toEqual('MY_FIELD is not a valid value.');
});
it('should format validArrayValues', () => {
const error = validate([2, 4], ValidatorsEx.validArrayValues([1, 2, 3]));
expect(error).toEqual('MY_FIELD contains an invalid value.');
});
it('should format match', () => {
const formControl1 = new FormControl(1);
const formControl2 = new FormControl(2);
const formGroup = new FormGroup({
field1: formControl1,
field2: formControl2
});
const formError = ValidatorsEx.match('field2', 'Passwords must match.')!(formControl1)!;
const formMessage = formatError('MY_FIELD', Object.keys(formError)[0], Object.values(formError)[0], undefined);
expect(formMessage).toEqual('Passwords must match.');
formGroup.reset();
});
function validate(value: any, validator: ValidatorFn) {
const formControl = new FormControl(value);
const formError = validator(formControl)!;
const formMessage = formatError('MY_FIELD', Object.keys(formError)[0], Object.values(formError)[0], value);
return formMessage;
}
});

70
src/Squidex/app/framework/angular/forms/error-formatting.ts

@ -0,0 +1,70 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights r vbeserved
*/
import { Types } from './../../utils/types';
const DEFAULT_ERRORS: { [key: string]: string } = {
between: '{field} must be between \'{min}\' and \'{max}\'.',
betweenlength: '{field} must have between {minlength} and {maxlength} item(s).',
betweenlengthstring: '{field} must have between {minlength} and {maxlength} character(s).',
email: '{field} must be an email address.',
exactly: '{field} must be exactly \'{expected}\'.',
exactlylength: '{field} must have exactly {expected} item(s).',
exactlylengthstring: '{field} must have exactly {expected} character(s).',
match: '{message}',
max: '{field} must be less or equal to \'{max}\'.',
maxlength: '{field} must not have more than {requiredlength} item(s).',
maxlengthstring: '{field} must not have more than {requiredlength} character(s).',
min: '{field} must be greater or equal to \'{min}\'.',
minlength: '{field} must have at least {requiredlength} item(s).',
minlengthstring: '{field} must have at least {requiredlength} character(s).',
pattern: '{field} does not match to the pattern.',
patternmessage: '{message}',
required: '{field} is required.',
requiredTrue: '{field} is required.',
validdatetime: '{field} is not a valid date time.',
validvalues: '{field} is not a valid value.',
validarrayvalues: '{field} contains an invalid value.'
};
export function formatError(field: string, type: string, properties: any, value: any, errors?: any) {
type = type.toLowerCase();
if (Types.isString(value)) {
if (type === 'minlength') {
type = 'minlengthstring';
}
if (type === 'maxlength') {
type = 'maxlengthstring';
}
if (type === 'exactlylength') {
type = 'exactlylengthstring';
}
if (type === 'betweenlength') {
type = 'betweenlengthstring';
}
}
let message = (errors ? errors[type] : null) || DEFAULT_ERRORS[type];
if (!message) {
return null;
}
for (let property in properties) {
if (properties.hasOwnProperty(property)) {
message = message.replace(`{${property.toLowerCase()}}`, properties[property]);
}
}
message = message.replace('{field}', field);
return message;
}

122
src/Squidex/app/framework/angular/forms/validators.spec.ts

@ -18,6 +18,14 @@ describe('ValidatorsEx.between', () => {
expect(validator).toBe(Validators.nullValidator);
});
it('should return null when value is equal to min and max', () => {
const input = new FormControl(3);
const error = <any>ValidatorsEx.between(3, 3)(input);
expect(error).toBeNull();
});
it('should return null when value is valid', () => {
const input = new FormControl(4);
@ -42,28 +50,108 @@ describe('ValidatorsEx.between', () => {
expect(error).toBeNull();
});
it('should return error when not a number', () => {
const input = new FormControl('text');
it('should return error if less than min', () => {
const input = new FormControl(0);
const error = <any>ValidatorsEx.between(1, 5)(input);
const error = <any>ValidatorsEx.between(1, undefined)(input);
expect(error.validnumber).toBeFalsy();
expect(error.min).toBeDefined();
});
it('should return error if less than minimum setting', () => {
const input = new FormControl(-5);
it('should return error if greater than max', () => {
const input = new FormControl(6);
const error = <any>ValidatorsEx.between(1, 5)(input);
const error = <any>ValidatorsEx.between(undefined, 5)(input);
expect(error.minvalue).toBeDefined();
expect(error.max).toBeDefined();
});
it('should return error if greater than maximum setting', () => {
const input = new FormControl(300);
it('should return error if not in range', () => {
const input = new FormControl(1);
const error = <any>ValidatorsEx.between(1, 5)(input);
const error = <any>ValidatorsEx.between(2, 4)(input);
expect(error.between).toBeDefined();
});
it('should return error if not equal to min and max', () => {
const input = new FormControl(2);
const error = <any>ValidatorsEx.between(3, 3)(input);
expect(error.exactly).toBeDefined();
});
});
describe('ValidatorsEx.betweenLength', () => {
it('should return null validator if no min value or max value', () => {
const validator = ValidatorsEx.betweenLength(undefined, undefined);
expect(validator).toBe(Validators.nullValidator);
});
it('should return null when value is equal to min and max', () => {
const input = new FormControl('xxx');
const error = <any>ValidatorsEx.betweenLength(3, 3)(input);
expect(error).toBeNull();
});
it('should return null when value is valid', () => {
const input = new FormControl('xxxx');
const error = <any>ValidatorsEx.betweenLength(1, 5)(input);
expect(error).toBeNull();
});
it('should return null when value is null', () => {
const input = new FormControl(null);
const error = <any>ValidatorsEx.betweenLength(1, 5)(input);
expect(error.maxvalue).toBeDefined();
expect(error).toBeNull();
});
it('should return null when value is undefined', () => {
const input = new FormControl(undefined);
const error = <any>ValidatorsEx.betweenLength(1, 5)(input);
expect(error).toBeNull();
});
it('should return error if less than min', () => {
const input = new FormControl('x');
const error = <any>ValidatorsEx.betweenLength(2, undefined)(input);
expect(error.minlength).toBeDefined();
});
it('should return error if greater than max', () => {
const input = new FormControl('xxxxxx');
const error = <any>ValidatorsEx.betweenLength(undefined, 5)(input);
expect(error.maxlength).toBeDefined();
});
it('should return error if not in range', () => {
const input = new FormControl('x');
const error = <any>ValidatorsEx.betweenLength(2, 4)(input);
expect(error.betweenlength).toBeDefined();
});
it('should return error if not equal to min and max', () => {
const input = new FormControl('xx');
const error = <any>ValidatorsEx.betweenLength(3, 3)(input);
expect(error.exactlylength).toBeDefined();
});
});
@ -283,14 +371,4 @@ describe('ValidatorsEx.pattern', () => {
expect(error).toEqual(expected);
});
});
describe('ValidatorsEx.noop', () => {
it('should return null validator', () => {
const input = new FormControl(null);
const error = <any>ValidatorsEx.noop()(input);
expect(error).toBeNull();
});
});

111
src/Squidex/app/framework/angular/forms/validators.ts

@ -7,7 +7,11 @@
import { AbstractControl, ValidatorFn, Validators } from '@angular/forms';
import { DateTime, Types } from '@app/framework/internal';
import { DateTime } from '@app/framework/internal';
function isEmptyInputValue(value: any): boolean {
return value == null || value.length === 0;
}
export module ValidatorsEx {
export function pattern(regex: string | RegExp, message?: string): ValidatorFn {
@ -15,33 +19,16 @@ export module ValidatorsEx {
return Validators.nullValidator;
}
let regeExp: RegExp;
let regexStr: string;
if (Types.isString(regex)) {
regexStr = `^${regex}$`;
regeExp = new RegExp(regexStr);
} else {
regexStr = regex.toString();
regeExp = regex;
}
const inner = Validators.pattern(regex);
return (control: AbstractControl) => {
const n: string = control.value;
const error = inner(control);
if (n == null || n.length === 0) {
return null;
if (error !== null && error.pattern && message) {
return { patternmessage: { requiredPattern: error.pattern.requiredPattern, actualValue: error.pattern.actualValue, message } };
}
if (!regeExp.test(n)) {
if (message) {
return { patternmessage: { requiredPattern: regexStr, actualValue: n, message } };
} else {
return { pattern: { requiredPattern: regexStr, actualValue: n } };
}
}
return null;
return error;
};
}
@ -89,26 +76,68 @@ export module ValidatorsEx {
};
}
export function between(minValue?: number, maxValue?: number): ValidatorFn {
if (!minValue || !maxValue) {
export function between(min?: number, max?: number): ValidatorFn {
if (!min && !max) {
return Validators.nullValidator;
}
return (control: AbstractControl) => {
const value: number = control.value;
if (!Types.isUndefined(value) && !Types.isNull(value)) {
if (!Types.isNumber(value)) {
return { validnumber: false };
} else if (minValue && value < minValue) {
return { minvalue: { minValue, actualValue: value } };
} else if (maxValue && value > maxValue) {
return { maxvalue: { maxValue, actualValue: value } };
if (max && min) {
return (control: AbstractControl) => {
if (isEmptyInputValue(control.value)) {
return null;
}
}
return null;
};
const value = parseFloat(control.value);
if (min === max) {
if (isNaN(value) || value !== min) {
return { exactly: { expected: min, actual: value } };
}
} else {
if (isNaN(value) || value < min || value > max) {
return { between: { min: min, max: max, actual: value }};
}
}
return null;
};
} else if (max) {
return Validators.max(max);
} else {
return Validators.min(min!);
}
}
export function betweenLength(minLength?: number, maxLength?: number): ValidatorFn {
if (!minLength && !maxLength) {
return Validators.nullValidator;
}
if (maxLength && minLength) {
return (control: AbstractControl) => {
if (isEmptyInputValue(control.value)) {
return null;
}
const length: number = control.value ? control.value.length : 0;
if (minLength === maxLength) {
if (isNaN(length) || length !== minLength) {
return { exactlylength: { expected: minLength, actual: length } };
}
} else {
if (isNaN(length) || length < minLength || length > maxLength) {
return { betweenlength: { minLength, maxLength, actual: length }};
}
}
return null;
};
} else if (maxLength) {
return Validators.maxLength(maxLength);
} else {
return Validators.minLength(minLength!);
}
}
export function validValues<T>(values: T[]): ValidatorFn {
@ -146,10 +175,4 @@ export module ValidatorsEx {
return null;
};
}
export function noop(): ValidatorFn {
return () => {
return null;
};
}
}

12
src/Squidex/app/shared/state/contents.forms.spec.ts

@ -121,7 +121,7 @@ describe('ArrayField', () => {
const field = createField(new ArrayFieldPropertiesDto({ isRequired: true, minItems: 1, maxItems: 5 }));
it('should create validators', () => {
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(3);
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(2);
});
it('should format to empty string if null', () => {
@ -145,7 +145,7 @@ describe('AssetsField', () => {
const field = createField(new AssetsFieldPropertiesDto({ isRequired: true, minItems: 1, maxItems: 5 }));
it('should create validators', () => {
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(3);
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(2);
});
it('should format to empty string if null', () => {
@ -169,7 +169,7 @@ describe('TagsField', () => {
const field = createField(new TagsFieldPropertiesDto('Tags', { isRequired: true, minItems: 1, maxItems: 5 }));
it('should create validators', () => {
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(3);
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(2);
});
it('should format to empty string if null', () => {
@ -306,7 +306,7 @@ describe('NumberField', () => {
const field = createField(new NumberFieldPropertiesDto('Input', { isRequired: true, minValue: 1, maxValue: 6, allowedValues: [1, 3] }));
it('should create validators', () => {
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(4);
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(3);
});
it('should format to empty string if null', () => {
@ -328,7 +328,7 @@ describe('ReferencesField', () => {
const field = createField(new ReferencesFieldPropertiesDto({ isRequired: true, minItems: 1, maxItems: 5 }));
it('should create validators', () => {
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(3);
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(2);
});
it('should format to empty string if null', () => {
@ -352,7 +352,7 @@ describe('StringField', () => {
const field = createField(new StringFieldPropertiesDto('Input', { isRequired: true, pattern: 'pattern', minLength: 1, maxLength: 5, allowedValues: ['a', 'b'] }));
it('should create validators', () => {
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(5);
expect(FieldValidatorsFactory.createValidators(field, false).length).toBe(4);
});
it('should format to empty string if null', () => {

72
src/Squidex/app/shared/state/contents.forms.ts

@ -146,15 +146,9 @@ export class FieldValidatorsFactory implements FieldPropertiesVisitor<ValidatorF
}
public visitNumber(properties: NumberFieldPropertiesDto): ValidatorFn[] {
const validators: ValidatorFn[] = [];
if (properties.minValue) {
validators.push(Validators.min(properties.minValue));
}
if (properties.maxValue) {
validators.push(Validators.max(properties.maxValue));
}
const validators: ValidatorFn[] = [
ValidatorsEx.betweenLength(properties.minValue, properties.maxValue)
];
if (properties.allowedValues && properties.allowedValues.length > 0) {
const values: (number | null)[] = properties.allowedValues;
@ -170,15 +164,9 @@ export class FieldValidatorsFactory implements FieldPropertiesVisitor<ValidatorF
}
public visitString(properties: StringFieldPropertiesDto): ValidatorFn[] {
const validators: ValidatorFn[] = [];
if (properties.minLength) {
validators.push(Validators.minLength(properties.minLength));
}
if (properties.maxLength) {
validators.push(Validators.maxLength(properties.maxLength));
}
const validators: ValidatorFn[] = [
ValidatorsEx.betweenLength(properties.minLength, properties.maxLength)
];
if (properties.pattern && properties.pattern.length > 0) {
validators.push(ValidatorsEx.pattern(properties.pattern, properties.patternMessage));
@ -198,57 +186,33 @@ export class FieldValidatorsFactory implements FieldPropertiesVisitor<ValidatorF
}
public visitArray(properties: ArrayFieldPropertiesDto): ValidatorFn[] {
const validators: ValidatorFn[] = [];
if (properties.minItems) {
validators.push(Validators.minLength(properties.minItems));
}
if (properties.maxItems) {
validators.push(Validators.maxLength(properties.maxItems));
}
const validators: ValidatorFn[] = [
ValidatorsEx.betweenLength(properties.minItems, properties.maxItems)
];
return validators;
}
public visitAssets(properties: AssetsFieldPropertiesDto): ValidatorFn[] {
const validators: ValidatorFn[] = [];
if (properties.minItems) {
validators.push(Validators.minLength(properties.minItems));
}
if (properties.maxItems) {
validators.push(Validators.maxLength(properties.maxItems));
}
const validators: ValidatorFn[] = [
ValidatorsEx.betweenLength(properties.minItems, properties.maxItems)
];
return validators;
}
public visitReferences(properties: ReferencesFieldPropertiesDto): ValidatorFn[] {
const validators: ValidatorFn[] = [];
if (properties.minItems) {
validators.push(Validators.minLength(properties.minItems));
}
if (properties.maxItems) {
validators.push(Validators.maxLength(properties.maxItems));
}
const validators: ValidatorFn[] = [
ValidatorsEx.betweenLength(properties.minItems, properties.maxItems)
];
return validators;
}
public visitTags(properties: TagsFieldPropertiesDto): ValidatorFn[] {
const validators: ValidatorFn[] = [];
if (properties.minItems) {
validators.push(Validators.minLength(properties.minItems));
}
if (properties.maxItems) {
validators.push(Validators.maxLength(properties.maxItems));
}
const validators: ValidatorFn[] = [
ValidatorsEx.betweenLength(properties.minItems, properties.maxItems)
];
if (properties.allowedValues && properties.allowedValues.length > 0) {
const values: (string | null)[] = properties.allowedValues;

20
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ArrayFieldTests.cs

@ -28,7 +28,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
}
[Fact]
public async Task Should_not_add_error_if_tags_are_valid()
public async Task Should_not_add_error_if_items_are_valid()
{
var sut = Field(new ArrayFieldProperties());
@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
}
[Fact]
public async Task Should_not_add_error_if_tags_are_null_and_valid()
public async Task Should_not_add_error_if_items_are_null_and_valid()
{
var sut = Field(new ArrayFieldProperties());
@ -48,7 +48,17 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
}
[Fact]
public async Task Should_add_error_if_tags_are_required_and_null()
public async Task Should_not_add_error_if_number_of_items_is_equal_to_min_and_max_items()
{
var sut = Field(new ArrayFieldProperties { MinItems = 2, MaxItems = 2 });
await sut.ValidateAsync(CreateValue(JsonValue.Object(), JsonValue.Object()), errors);
Assert.Empty(errors);
}
[Fact]
public async Task Should_add_error_if_items_are_required_and_null()
{
var sut = Field(new ArrayFieldProperties { IsRequired = true });
@ -59,7 +69,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
}
[Fact]
public async Task Should_add_error_if_tags_are_required_and_empty()
public async Task Should_add_error_if_items_are_required_and_empty()
{
var sut = Field(new ArrayFieldProperties { IsRequired = true });
@ -99,7 +109,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
await sut.ValidateAsync(CreateValue(JsonValue.Object(), JsonValue.Object()), errors);
errors.Should().BeEquivalentTo(
new[] { "Must have not more than 1 item(s)." });
new[] { "Must not have more than 1 item(s)." });
}
private static IJsonValue CreateValue(params JsonObject[] ids)

12
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs

@ -92,6 +92,16 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
Assert.Empty(errors);
}
[Fact]
public async Task Should_not_add_error_if_number_of_assets_is_equal_to_min_and_max_items()
{
var sut = Field(new AssetsFieldProperties { MinItems = 2, MaxItems = 2 });
await sut.ValidateAsync(CreateValue(document.AssetId, document.AssetId), errors, ctx);
Assert.Empty(errors);
}
[Fact]
public async Task Should_add_error_if_assets_are_required_and_null()
{
@ -144,7 +154,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
await sut.ValidateAsync(CreateValue(document.AssetId, document.AssetId), errors, ctx);
errors.Should().BeEquivalentTo(
new[] { "Must have not more than 1 item(s)." });
new[] { "Must not have more than 1 item(s)." });
}
[Fact]

4
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs

@ -59,7 +59,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("my-field: Must be less than or equal to '100'.", "my-field")
new ValidationError("my-field: Must be less or equal to '100'.", "my-field")
});
}
@ -221,7 +221,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("my-field: Must be less than or equal to '100'.", "my-field")
new ValidationError("my-field: Must be less or equal to '100'.", "my-field")
});
}

4
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/DateTimeFieldTests.cs

@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
await sut.ValidateAsync(CreateValue(FutureDays(0)), errors);
errors.Should().BeEquivalentTo(
new[] { $"Must be greater than or equal to '{FutureDays(10)}'." });
new[] { $"Must be greater or equal to '{sut.Properties.MinValue}'." });
}
[Fact]
@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
await sut.ValidateAsync(CreateValue(FutureDays(20)), errors);
errors.Should().BeEquivalentTo(
new[] { $"Must be less than or equal to '{FutureDays(10)}'." });
new[] { $"Must be less or equal to '{FutureDays(10)}'." });
}
[Fact]

4
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs

@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
await sut.ValidateAsync(CreateValue(5), errors);
errors.Should().BeEquivalentTo(
new[] { "Must be greater than or equal to '10'." });
new[] { "Must be greater or equal to '10'." });
}
[Fact]
@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
await sut.ValidateAsync(CreateValue(20), errors);
errors.Should().BeEquivalentTo(
new[] { "Must be less than or equal to '10'." });
new[] { "Must be less or equal to '10'." });
}
[Fact]

12
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs

@ -51,6 +51,16 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
Assert.Empty(errors);
}
[Fact]
public async Task Should_not_add_error_if_number_of_references_is_equal_to_min_and_max_items()
{
var sut = Field(new ReferencesFieldProperties { MinItems = 2, MaxItems = 2 });
await sut.ValidateAsync(CreateValue(ref1, ref2), errors);
Assert.Empty(errors);
}
[Fact]
public async Task Should_add_error_if_references_are_required_and_null()
{
@ -103,7 +113,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
await sut.ValidateAsync(CreateValue(ref1, ref2), errors, ValidationTestExtensions.References(ref1, ref2));
errors.Should().BeEquivalentTo(
new[] { "Must have not more than 1 item(s)." });
new[] { "Must not have more than 1 item(s)." });
}
[Fact]

6
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs

@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
await sut.ValidateAsync(CreateValue("123"), errors);
errors.Should().BeEquivalentTo(
new[] { "Must have more than '10' characters." });
new[] { "Must have at least 10 character(s)." });
}
[Fact]
@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
await sut.ValidateAsync(CreateValue("12345678"), errors);
errors.Should().BeEquivalentTo(
new[] { "Must have less than '5' characters." });
new[] { "Must not have more than 5 character(s)." });
}
[Fact]
@ -90,7 +90,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
await sut.ValidateAsync(CreateValue("abc"), errors);
errors.Should().BeEquivalentTo(
new[] { "Not valid." });
new[] { "Does not match to the pattern." });
}
[Fact]

12
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs

@ -48,6 +48,16 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
Assert.Empty(errors);
}
[Fact]
public async Task Should_not_add_error_if_number_of_tags_is_equal_to_min_and_max_items()
{
var sut = Field(new TagsFieldProperties { MinItems = 2, MaxItems = 2 });
await sut.ValidateAsync(CreateValue("tag1", "tag2"), errors);
Assert.Empty(errors);
}
[Fact]
public async Task Should_add_error_if_tags_are_required_and_null()
{
@ -100,7 +110,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
await sut.ValidateAsync(CreateValue("tag-1", "tag-2"), errors);
errors.Should().BeEquivalentTo(
new[] { "Must have not more than 1 item(s)." });
new[] { "Must not have more than 1 item(s)." });
}
[Fact]

4
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/CollectionItemValidatorTests.cs

@ -47,8 +47,8 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators
errors.Should().BeEquivalentTo(
new[]
{
"[2]: Must be greater than or equal to '2'.",
"[4]: Must be less than or equal to '4'."
"[2]: Must be between '2' and '4'.",
"[4]: Must be between '2' and '4'."
});
}
}

36
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/CollectionValidatorTests.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FluentAssertions;
@ -17,6 +18,13 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators
{
private readonly List<string> errors = new List<string>();
[Theory]
[InlineData(20, 10)]
public void Should_throw_error_if_min_greater_than_max(int? min, int? max)
{
Assert.Throws<ArgumentException>(() => new CollectionValidator(true, min, max));
}
[Fact]
public async Task Should_not_add_error_if_value_is_valid()
{
@ -48,10 +56,21 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators
new[] { "Field is required." });
}
[Fact]
public async Task Should_add_error_if_collection_has_not_exact_number_of_items()
{
var sut = new CollectionValidator(true, 2, 2);
await sut.ValidateAsync(new List<int> { 1 }, errors);
errors.Should().BeEquivalentTo(
new[] { "Must have exactly 2 item(s)." });
}
[Fact]
public async Task Should_add_error_if_collection_has_too_few_items()
{
var sut = new CollectionValidator(true, 2, 3);
var sut = new CollectionValidator(true, 2, null);
await sut.ValidateAsync(new List<int> { 1 }, errors);
@ -62,12 +81,23 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators
[Fact]
public async Task Should_add_error_if_collection_has_too_many_items()
{
var sut = new CollectionValidator(true, 2, 3);
var sut = new CollectionValidator(true, null, 3);
await sut.ValidateAsync(new List<int> { 1, 2, 3, 4 }, errors);
errors.Should().BeEquivalentTo(
new[] { "Must have not more than 3 item(s)." });
new[] { "Must not have more than 3 item(s)." });
}
[Fact]
public async Task Should_add_error_if_collection_count_is_not_in_range()
{
var sut = new CollectionValidator(true, 2, 5);
await sut.ValidateAsync(new List<int> { 1 }, errors);
errors.Should().BeEquivalentTo(
new[] { "Must have between 2 and 5 item(s)." });
}
}
}

2
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/PatternValidatorTests.cs

@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators
await sut.ValidateAsync("foo", errors);
errors.Should().BeEquivalentTo(
new[] { "Not valid." });
new[] { "Does not match to the pattern." });
}
[Fact]

16
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/RangeValidatorTests.cs

@ -44,12 +44,22 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators
[Theory]
[InlineData(20, 10)]
[InlineData(10, 10)]
public void Should_throw_error_if_min_greater_than_max(int? min, int? max)
{
Assert.Throws<ArgumentException>(() => new RangeValidator<int>(min, max));
}
[Fact]
public async Task Should_add_error_if_value_is_not_equal_to_min_and_max()
{
var sut = new RangeValidator<int>(2000, 2000);
await sut.ValidateAsync(1500, errors);
errors.Should().BeEquivalentTo(
new[] { "Must be exactly '2000'." });
}
[Fact]
public async Task Should_add_error_if_value_is_smaller_than_min()
{
@ -58,7 +68,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators
await sut.ValidateAsync(1500, errors);
errors.Should().BeEquivalentTo(
new[] { "Must be greater than or equal to '2000'." });
new[] { "Must be greater or equal to '2000'." });
}
[Fact]
@ -69,7 +79,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators
await sut.ValidateAsync(1500, errors);
errors.Should().BeEquivalentTo(
new[] { "Must be less than or equal to '1000'." });
new[] { "Must be less or equal to '1000'." });
}
}
}

27
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/StringLengthValidatorTests.cs

@ -55,12 +55,22 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators
[Theory]
[InlineData(20, 10)]
[InlineData(10, 10)]
public void Should_throw_error_if_min_greater_than_max(int? min, int? max)
{
Assert.Throws<ArgumentException>(() => new StringLengthValidator(min, max));
}
[Fact]
public async Task Should_add_error_if_collection_has_not_exact_number_of_items()
{
var sut = new StringLengthValidator(2000, 2000);
await sut.ValidateAsync(CreateString(4), errors);
errors.Should().BeEquivalentTo(
new[] { "Must have exactly 2000 character(s)." });
}
[Fact]
public async Task Should_add_error_if_value_is_smaller_than_min()
{
@ -69,7 +79,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators
await sut.ValidateAsync(CreateString(1500), errors);
errors.Should().BeEquivalentTo(
new[] { "Must have more than '2000' characters." });
new[] { "Must have at least 2000 character(s)." });
}
[Fact]
@ -80,7 +90,18 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators
await sut.ValidateAsync(CreateString(1500), errors);
errors.Should().BeEquivalentTo(
new[] { "Must have less than '1000' characters." });
new[] { "Must not have more than 1000 character(s)." });
}
[Fact]
public async Task Should_add_error_if_collection_count_is_not_in_range()
{
var sut = new StringLengthValidator(2000, 5000);
await sut.ValidateAsync(CreateString(1), errors);
errors.Should().BeEquivalentTo(
new[] { "Must have between 2000 and 5000 character(s)." });
}
private static string CreateString(int size)

2
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppClientsTests.cs

@ -113,7 +113,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
var clients_1 = clients_0.Add("ios", "secret");
ValidationAssert.Throws(() => GuardAppClients.CanUpdate(clients_1, command, roles),
new ValidationError("Role is not valid.", "Role"));
new ValidationError("Role is not a valid value.", "Role"));
}
[Fact]

2
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs

@ -65,7 +65,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
var command = new AssignContributor { ContributorId = "1", Role = "Invalid" };
return ValidationAssert.ThrowsAsync(() => GuardAppContributors.CanAssign(contributors_0, command, users, appPlan, roles),
new ValidationError("Role is not valid.", "Role"));
new ValidationError("Role is not a valid value.", "Role"));
}
[Fact]

2
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppLanguagesTests.cs

@ -90,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
var languages_1 = languages_0.Set(new LanguageConfig(Language.EN));
ValidationAssert.Throws(() => GuardAppLanguages.CanUpdate(languages_1, command),
new ValidationError("Language is required.", "Language"));
new ValidationError("Language code is required.", "Language"));
}
[Fact]

4
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppPatternsTests.cs

@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
var command = new AddPattern { PatternId = patternId, Name = "any", Pattern = "[0-9{1}" };
ValidationAssert.Throws(() => GuardAppPatterns.CanAdd(patterns_0, command),
new ValidationError("Pattern is not a valid regular expression.", "Pattern"));
new ValidationError("Pattern is not a valid value.", "Pattern"));
}
[Fact]
@ -126,7 +126,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
var command = new UpdatePattern { PatternId = patternId, Name = "any", Pattern = "[0-9{1}" };
ValidationAssert.Throws(() => GuardAppPatterns.CanUpdate(patterns_1, command),
new ValidationError("Pattern is not a valid regular expression.", "Pattern"));
new ValidationError("Pattern is not a valid value.", "Pattern"));
}
[Fact]

2
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppTests.cs

@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
var command = new CreateApp { Name = "INVALID NAME" };
ValidationAssert.Throws(() => GuardApp.CanCreate(command),
new ValidationError("Name must be a valid slug.", "Name"));
new ValidationError("Name is not a valid slug.", "Name"));
}
[Fact]

2
tests/Squidex.Domain.Apps.Entities.Tests/Contents/Guard/GuardContentTests.cs

@ -99,7 +99,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
var command = new ChangeContentStatus { Status = (Status)10 };
ValidationAssert.Throws(() => GuardContent.CanChangeContentStatus(schema, false, Status.Archived, command),
new ValidationError("Status is not valid.", "Status"));
new ValidationError("Status is not a valid value.", "Status"));
}
[Fact]

12
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/ArrayFieldPropertiesTests.cs

@ -26,8 +26,18 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Max items must be greater than min items.", "MinItems", "MaxItems")
new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems")
});
}
[Fact]
public void Should_not_add_error_if_min_items_equals_to_max_items()
{
var sut = new ArrayFieldProperties { MinItems = 2, MaxItems = 2 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
Assert.Empty(errors);
}
}
}

40
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/AssetsFieldPropertiesTests.cs

@ -26,10 +26,20 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Max items must be greater than min items.", "MinItems", "MaxItems")
new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems")
});
}
[Fact]
public void Should_not_add_error_if_min_equals_to_max_items()
{
var sut = new AssetsFieldProperties { MinItems = 2, MaxItems = 2 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
Assert.Empty(errors);
}
[Fact]
public void Should_add_error_if_min_width_greater_than_max_width()
{
@ -40,10 +50,20 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Max width must be greater than min width.", "MinWidth", "MaxWidth")
new ValidationError("Max width must be greater or equal to min width.", "MinWidth", "MaxWidth")
});
}
[Fact]
public void Should_not_add_error_if_min_width_equals_to_max_width()
{
var sut = new AssetsFieldProperties { MinWidth = 2, MaxWidth = 2 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
Assert.Empty(errors);
}
[Fact]
public void Should_add_error_if_min_height_greater_than_max_height()
{
@ -54,10 +74,20 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Max height must be greater than min height.", "MinHeight", "MaxHeight")
new ValidationError("Max height must be greater or equal to min height.", "MinHeight", "MaxHeight")
});
}
[Fact]
public void Should_not_add_error_if_min_height_equals_to_max_height()
{
var sut = new AssetsFieldProperties { MinHeight = 2, MaxHeight = 2 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
Assert.Empty(errors);
}
[Fact]
public void Should_add_error_if_min_size_greater_than_max_size()
{
@ -82,7 +112,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Aspect width and height is required.", "AspectWidth", "AspectHeight")
new ValidationError("If aspect width or aspect height is used both must be defined.", "AspectWidth", "AspectHeight")
});
}
@ -96,7 +126,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Aspect width and height is required.", "AspectWidth", "AspectHeight")
new ValidationError("If aspect width or aspect height is used both must be defined.", "AspectWidth", "AspectHeight")
});
}
}

6
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/DateTimeFieldPropertiesTests.cs

@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Default value must be greater than min value.", "DefaultValue")
new ValidationError("Default value must be greater or equal to min value.", "DefaultValue")
});
}
@ -56,7 +56,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Default value must be less than max value.", "DefaultValue")
new ValidationError("Default value must be less or equal to max value.", "DefaultValue")
});
}
@ -98,7 +98,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Calculated default value is not valid.", "CalculatedDefaultValue")
new ValidationError("Calculated default value is not a valid value.", "CalculatedDefaultValue")
});
}

4
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/NumberFieldPropertiesTests.cs

@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Default value must be greater than min value.", "DefaultValue")
new ValidationError("Default value must be greater or equal to min value.", "DefaultValue")
});
}
@ -56,7 +56,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Default value must be less than max value.", "DefaultValue")
new ValidationError("Default value must be less or equal to max value.", "DefaultValue")
});
}

14
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/ReferencesFieldPropertiesTests.cs

@ -17,7 +17,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
public class ReferencesFieldPropertiesTests
{
[Fact]
public void Should_add_error_if_min_greater_than_max()
public void Should_add_error_if_min_items_greater_than_max_items()
{
var sut = new ReferencesFieldProperties { MinItems = 10, MaxItems = 5 };
@ -26,8 +26,18 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Max items must be greater than min items.", "MinItems", "MaxItems")
new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems")
});
}
[Fact]
public void Should_not_add_error_if_min_items_greater_equals_to_max_items()
{
var sut = new ReferencesFieldProperties { MinItems = 2, MaxItems = 2 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
Assert.Empty(errors);
}
}
}

14
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/StringFieldPropertiesTests.cs

@ -27,10 +27,20 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Max length must be greater than min length.", "MinLength", "MaxLength")
new ValidationError("Max length must be greater or equal to min length.", "MinLength", "MaxLength")
});
}
[Fact]
public void Should_not_add_error_if_min_length_equal_to_max_length()
{
var sut = new StringFieldProperties { MinLength = 2, MaxLength = 2 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
Assert.Empty(errors);
}
[Fact]
public void Should_add_error_if_allowed_values_and_max_value_is_specified()
{
@ -97,7 +107,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Pattern is not a valid expression.", "Pattern")
new ValidationError("Pattern is not a valid value.", "Pattern")
});
}

14
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/TagsFieldPropertiesTests.cs

@ -17,7 +17,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
public class TagsFieldPropertiesTests
{
[Fact]
public void Should_add_error_if_min_greater_than_max()
public void Should_add_error_if_min_items_greater_than_max_items()
{
var sut = new TagsFieldProperties { MinItems = 10, MaxItems = 5 };
@ -26,10 +26,20 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Max items must be greater than min items.", "MinItems", "MaxItems")
new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems")
});
}
[Fact]
public void Should_not_add_error_if_min_items_equal_to_max_items()
{
var sut = new TagsFieldProperties { MinItems = 2, MaxItems = 2 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
Assert.Empty(errors);
}
[Fact]
public void Should_add_error_if_radio_button_has_no_allowed_values()
{

6
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaFieldTests.cs

@ -214,7 +214,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
var command = new UpdateField { FieldId = 2, Properties = new StringFieldProperties { MinLength = 10, MaxLength = 5 } };
ValidationAssert.Throws(() => GuardSchemaField.CanUpdate(schema_0, command),
new ValidationError("Max length must be greater than min length.", "Properties.MinLength", "Properties.MaxLength"));
new ValidationError("Max length must be greater or equal to min length.", "Properties.MinLength", "Properties.MaxLength"));
}
[Fact]
@ -250,7 +250,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
var command = new AddField { Name = "field4", Properties = invalidProperties };
ValidationAssert.Throws(() => GuardSchemaField.CanAdd(schema_0, command),
new ValidationError("Max length must be greater than min length.", "Properties.MinLength", "Properties.MaxLength"));
new ValidationError("Max length must be greater or equal to min length.", "Properties.MinLength", "Properties.MaxLength"));
}
[Fact]
@ -268,7 +268,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
var command = new AddField { Name = "field4", Partitioning = "INVALID_PARTITIONING", Properties = validProperties };
ValidationAssert.Throws(() => GuardSchemaField.CanAdd(schema_0, command),
new ValidationError("Partitioning is not valid.", "Partitioning"));
new ValidationError("Partitioning is not a valid value.", "Partitioning"));
}
[Fact]

6
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs

@ -123,7 +123,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
};
return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider),
new ValidationError("Max length must be greater than min length.",
new ValidationError("Max length must be greater or equal to min length.",
"Fields[1].Properties.MinLength",
"Fields[1].Properties.MaxLength"));
}
@ -147,7 +147,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
};
return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider),
new ValidationError("Field partitioning is not valid.",
new ValidationError("Partitioning is not a valid value.",
"Fields[1].Partitioning"));
}
@ -300,7 +300,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
};
return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider),
new ValidationError("Max length must be greater than min length.",
new ValidationError("Max length must be greater or equal to min length.",
"Fields[1].Nested[1].Properties.MinLength",
"Fields[1].Nested[1].Properties.MaxLength"));
}

Loading…
Cancel
Save