mirror of https://github.com/Squidex/squidex.git
10 changed files with 258 additions and 1 deletions
@ -0,0 +1,50 @@ |
|||||
|
// ==========================================================================
|
||||
|
// TagsField.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using Squidex.Domain.Apps.Core.Schemas.Validators; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Schemas |
||||
|
{ |
||||
|
public sealed class TagsField : Field<TagsFieldProperties> |
||||
|
{ |
||||
|
private static readonly ImmutableList<string> EmptyTags = ImmutableList<string>.Empty; |
||||
|
|
||||
|
public TagsField(long id, string name, Partitioning partitioning) |
||||
|
: this(id, name, partitioning, new TagsFieldProperties()) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public TagsField(long id, string name, Partitioning partitioning, TagsFieldProperties properties) |
||||
|
: base(id, name, partitioning, properties) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override IEnumerable<IValidator> CreateValidators() |
||||
|
{ |
||||
|
if (Properties.IsRequired || Properties.MinItems.HasValue || Properties.MaxItems.HasValue) |
||||
|
{ |
||||
|
yield return new CollectionValidator<string>(Properties.IsRequired, Properties.MinItems, Properties.MaxItems); |
||||
|
} |
||||
|
|
||||
|
yield return new CollectionItemValidator<string>(new RequiredStringValidator()); |
||||
|
} |
||||
|
|
||||
|
public override object ConvertValue(JToken value) |
||||
|
{ |
||||
|
return value.ToObject<List<string>>(); |
||||
|
} |
||||
|
|
||||
|
public override T Accept<T>(IFieldVisitor<T> visitor) |
||||
|
{ |
||||
|
return visitor.Visit(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
// ==========================================================================
|
||||
|
// TagsField.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Schemas |
||||
|
{ |
||||
|
[TypeName(nameof(TagsField))] |
||||
|
public sealed class TagsFieldProperties : FieldProperties |
||||
|
{ |
||||
|
private int? minItems; |
||||
|
private int? maxItems; |
||||
|
|
||||
|
public int? MinItems |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return minItems; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
ThrowIfFrozen(); |
||||
|
|
||||
|
minItems = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int? MaxItems |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return maxItems; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
ThrowIfFrozen(); |
||||
|
|
||||
|
maxItems = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override JToken GetDefaultValue() |
||||
|
{ |
||||
|
return new JArray(); |
||||
|
} |
||||
|
|
||||
|
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor) |
||||
|
{ |
||||
|
return visitor.Visit(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,121 @@ |
|||||
|
// ==========================================================================
|
||||
|
// TagsFieldTests.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using FluentAssertions; |
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Schemas |
||||
|
{ |
||||
|
public class TagsFieldTests |
||||
|
{ |
||||
|
private readonly List<string> errors = new List<string>(); |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_instantiate_field() |
||||
|
{ |
||||
|
var sut = new TagsField(1, "my-tags", Partitioning.Invariant); |
||||
|
|
||||
|
Assert.Equal("my-tags", sut.Name); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_clone_object() |
||||
|
{ |
||||
|
var sut = new TagsField(1, "my-tags", Partitioning.Invariant); |
||||
|
|
||||
|
Assert.NotEqual(sut, sut.Enable()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_add_error_if_tags_are_valid() |
||||
|
{ |
||||
|
var referenceId = Guid.NewGuid(); |
||||
|
|
||||
|
var sut = new TagsField(1, "my-tags", Partitioning.Invariant); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue(referenceId), errors, ValidationTestExtensions.ValidContext); |
||||
|
|
||||
|
Assert.Empty(errors); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_add_error_if_tags_are_null_and_valid() |
||||
|
{ |
||||
|
var sut = new TagsField(1, "my-tags", Partitioning.Invariant); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue(null), errors); |
||||
|
|
||||
|
Assert.Empty(errors); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_tags_are_required_and_null() |
||||
|
{ |
||||
|
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { IsRequired = true }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue(null), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "<FIELD> is required." }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_tags_are_required_and_empty() |
||||
|
{ |
||||
|
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { IsRequired = true }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue(), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "<FIELD> is required." }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_value_is_not_valid() |
||||
|
{ |
||||
|
var sut = new TagsField(1, "my-tags", Partitioning.Invariant); |
||||
|
|
||||
|
await sut.ValidateAsync("invalid", errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "<FIELD> is not a valid value." }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_value_has_not_enough_items() |
||||
|
{ |
||||
|
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { MinItems = 3 }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "<FIELD> must have at least 3 item(s)." }); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_errors_if_value_has_too_much_items() |
||||
|
{ |
||||
|
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { MaxItems = 1 }); |
||||
|
|
||||
|
await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors); |
||||
|
|
||||
|
errors.ShouldBeEquivalentTo( |
||||
|
new[] { "<FIELD> must have not more than 1 item(s)." }); |
||||
|
} |
||||
|
|
||||
|
private static JToken CreateValue(params Guid[] ids) |
||||
|
{ |
||||
|
return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType<object>().ToArray()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue