Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a6615da76 | ||
|
|
a5d1b7c122 | ||
|
|
3001e6dbff | ||
|
|
b917b61dac | ||
|
|
74a517a2e1 | ||
|
|
706a0983df | ||
|
|
bb419ec2c6 | ||
|
|
018d49dda2 | ||
|
|
2fbdc291c9 | ||
|
|
9148a0f845 | ||
|
|
2f7383a622 | ||
|
|
b306c16d10 | ||
|
|
9288854d6b | ||
|
|
1565665a40 |
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Api.Management.ViewModels;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
using ContentTypeEditingModels = Umbraco.Cms.Core.Models.ContentTypeEditing;
|
||||
@@ -38,12 +39,27 @@ internal abstract class ContentTypeEditingPresentationFactory<TContentType>
|
||||
VariesByCulture = viewModel.VariesByCulture,
|
||||
VariesBySegment = viewModel.VariesBySegment,
|
||||
Containers = MapContainers<TPropertyTypeContainerEditingModel>(viewModel.Containers),
|
||||
Properties = MapProperties<TPropertyTypeEditingModel>(viewModel.Properties)
|
||||
Properties = MapProperties<TPropertyTypeEditingModel>(viewModel.Properties),
|
||||
};
|
||||
|
||||
return editingModel;
|
||||
}
|
||||
|
||||
protected Guid? CalculateCreateContainerKey(ReferenceByIdModel? parent, IDictionary<Guid, ContentTypeViewModels.CompositionType> compositions)
|
||||
{
|
||||
// special case:
|
||||
// the API is somewhat confusing when it comes to inheritance. the parent denotes a container (folder), but it
|
||||
// is easily confused with the parent for inheritance.
|
||||
// if the request model contains the same key for container and "inheritance composition", we'll be lenient and
|
||||
// allow it - just remove the container, inheritance takes precedence as intent.
|
||||
Guid? parentId = parent?.Id;
|
||||
return parentId.HasValue
|
||||
&& compositions.TryGetValue(parentId.Value, out ContentTypeViewModels.CompositionType compositionType)
|
||||
&& compositionType is ContentTypeViewModels.CompositionType.Inheritance
|
||||
? null
|
||||
: parentId;
|
||||
}
|
||||
|
||||
protected T MapCompositionModel<T>(ContentTypeAvailableCompositionsResult compositionResult)
|
||||
where T : ContentTypeViewModels.AvailableContentTypeCompositionResponseModelBase, new()
|
||||
{
|
||||
|
||||
@@ -25,12 +25,14 @@ internal sealed class DocumentTypeEditingPresentationFactory : ContentTypeEditin
|
||||
MapCleanup(createModel, requestModel.Cleanup);
|
||||
|
||||
createModel.Key = requestModel.Id;
|
||||
createModel.ContainerKey = requestModel.Parent?.Id;
|
||||
createModel.AllowedTemplateKeys = requestModel.AllowedTemplates.Select(reference => reference.Id).ToArray();
|
||||
createModel.DefaultTemplateKey = requestModel.DefaultTemplate?.Id;
|
||||
createModel.ListView = requestModel.Collection?.Id;
|
||||
createModel.AllowedContentTypes = MapAllowedContentTypes(requestModel.AllowedDocumentTypes);
|
||||
createModel.Compositions = MapCompositions(requestModel.Compositions);
|
||||
|
||||
IDictionary<Guid, ViewModels.ContentType.CompositionType> compositionTypesByKey = CompositionTypesByKey(requestModel.Compositions);
|
||||
createModel.Compositions = MapCompositions(compositionTypesByKey);
|
||||
createModel.ContainerKey = CalculateCreateContainerKey(requestModel.Parent, compositionTypesByKey);
|
||||
|
||||
return createModel;
|
||||
}
|
||||
@@ -51,7 +53,7 @@ internal sealed class DocumentTypeEditingPresentationFactory : ContentTypeEditin
|
||||
updateModel.DefaultTemplateKey = requestModel.DefaultTemplate?.Id;
|
||||
updateModel.ListView = requestModel.Collection?.Id;
|
||||
updateModel.AllowedContentTypes = MapAllowedContentTypes(requestModel.AllowedDocumentTypes);
|
||||
updateModel.Compositions = MapCompositions(requestModel.Compositions);
|
||||
updateModel.Compositions = MapCompositions(CompositionTypesByKey(requestModel.Compositions));
|
||||
|
||||
return updateModel;
|
||||
}
|
||||
@@ -72,8 +74,8 @@ internal sealed class DocumentTypeEditingPresentationFactory : ContentTypeEditin
|
||||
.DistinctBy(t => t.DocumentType.Id)
|
||||
.ToDictionary(t => t.DocumentType.Id, t => t.SortOrder));
|
||||
|
||||
private IEnumerable<Composition> MapCompositions(IEnumerable<DocumentTypeComposition> documentTypeCompositions)
|
||||
=> MapCompositions(documentTypeCompositions
|
||||
private IDictionary<Guid, ViewModels.ContentType.CompositionType> CompositionTypesByKey(IEnumerable<DocumentTypeComposition> documentTypeCompositions)
|
||||
=> documentTypeCompositions
|
||||
.DistinctBy(c => c.DocumentType.Id)
|
||||
.ToDictionary(c => c.DocumentType.Id, c => c.CompositionType));
|
||||
.ToDictionary(c => c.DocumentType.Id, c => c.CompositionType);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ internal sealed class DocumentVersionPresentationFactory : IDocumentVersionPrese
|
||||
new ReferenceByIdModel(_entityService.GetKey(contentVersion.ContentTypeId, UmbracoObjectTypes.DocumentType)
|
||||
.Result),
|
||||
new ReferenceByIdModel(await _userIdKeyResolver.GetAsync(contentVersion.UserId)),
|
||||
new DateTimeOffset(contentVersion.VersionDate, TimeSpan.Zero), // todo align with datetime offset rework
|
||||
new DateTimeOffset(contentVersion.VersionDate),
|
||||
contentVersion.CurrentPublishedVersion,
|
||||
contentVersion.CurrentDraftVersion,
|
||||
contentVersion.PreventCleanup);
|
||||
|
||||
@@ -23,11 +23,13 @@ internal sealed class MediaTypeEditingPresentationFactory : ContentTypeEditingPr
|
||||
>(requestModel);
|
||||
|
||||
createModel.Key = requestModel.Id;
|
||||
createModel.ContainerKey = requestModel.Parent?.Id;
|
||||
createModel.AllowedContentTypes = MapAllowedContentTypes(requestModel.AllowedMediaTypes);
|
||||
createModel.Compositions = MapCompositions(requestModel.Compositions);
|
||||
createModel.ListView = requestModel.Collection?.Id;
|
||||
|
||||
IDictionary<Guid, ViewModels.ContentType.CompositionType> compositionTypesByKey = CompositionTypesByKey(requestModel.Compositions);
|
||||
createModel.Compositions = MapCompositions(compositionTypesByKey);
|
||||
createModel.ContainerKey = CalculateCreateContainerKey(requestModel.Parent, compositionTypesByKey);
|
||||
|
||||
return createModel;
|
||||
}
|
||||
|
||||
@@ -42,7 +44,7 @@ internal sealed class MediaTypeEditingPresentationFactory : ContentTypeEditingPr
|
||||
>(requestModel);
|
||||
|
||||
updateModel.AllowedContentTypes = MapAllowedContentTypes(requestModel.AllowedMediaTypes);
|
||||
updateModel.Compositions = MapCompositions(requestModel.Compositions);
|
||||
updateModel.Compositions = MapCompositions(CompositionTypesByKey(requestModel.Compositions));
|
||||
updateModel.ListView = requestModel.Collection?.Id;
|
||||
|
||||
return updateModel;
|
||||
@@ -56,8 +58,8 @@ internal sealed class MediaTypeEditingPresentationFactory : ContentTypeEditingPr
|
||||
.DistinctBy(t => t.MediaType.Id)
|
||||
.ToDictionary(t => t.MediaType.Id, t => t.SortOrder));
|
||||
|
||||
private IEnumerable<Composition> MapCompositions(IEnumerable<MediaTypeComposition> documentTypeCompositions)
|
||||
=> MapCompositions(documentTypeCompositions
|
||||
private IDictionary<Guid, ViewModels.ContentType.CompositionType> CompositionTypesByKey(IEnumerable<MediaTypeComposition> documentTypeCompositions)
|
||||
=> documentTypeCompositions
|
||||
.DistinctBy(c => c.MediaType.Id)
|
||||
.ToDictionary(c => c.MediaType.Id, c => c.CompositionType));
|
||||
.ToDictionary(c => c.MediaType.Id, c => c.CompositionType);
|
||||
}
|
||||
|
||||
@@ -83,24 +83,10 @@ public abstract class ContentTypeMapDefinition<TContentType, TPropertyTypeModel,
|
||||
? CompositionType.Inheritance
|
||||
: CompositionType.Composition;
|
||||
|
||||
protected static IEnumerable<T> MapNestedCompositions<T>(IEnumerable<IContentTypeComposition> directCompositions, int contentTypeParentId, Func<ReferenceByIdModel, CompositionType, T> contentTypeCompositionFactory)
|
||||
{
|
||||
var allCompositions = new List<T>();
|
||||
|
||||
foreach (var composition in directCompositions)
|
||||
{
|
||||
CompositionType compositionType = CalculateCompositionType(contentTypeParentId, composition);
|
||||
T contentTypeComposition = contentTypeCompositionFactory(new ReferenceByIdModel(composition.Key), compositionType);
|
||||
allCompositions.Add(contentTypeComposition);
|
||||
|
||||
// When we have composition inheritance, we have to find all ancestor compositions recursively
|
||||
if (compositionType == CompositionType.Inheritance && composition.ContentTypeComposition.Any())
|
||||
{
|
||||
var nestedCompositions = MapNestedCompositions(composition.ContentTypeComposition, composition.ParentId, contentTypeCompositionFactory);
|
||||
allCompositions.AddRange(nestedCompositions);
|
||||
}
|
||||
}
|
||||
|
||||
return allCompositions;
|
||||
}
|
||||
protected static IEnumerable<T> MapCompositions<T>(IEnumerable<IContentTypeComposition> directCompositions, int contentTypeParentId, Func<ReferenceByIdModel, CompositionType, T> contentTypeCompositionFactory)
|
||||
=> directCompositions
|
||||
.Select(composition => contentTypeCompositionFactory(
|
||||
new ReferenceByIdModel(composition.Key),
|
||||
CalculateCompositionType(contentTypeParentId, composition)))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class DocumentTypeMapDefinition : ContentTypeMapDefinition<IContentType,
|
||||
new DocumentTypeSort { DocumentType = new ReferenceByIdModel(ct.Key), SortOrder = ct.SortOrder })
|
||||
.OrderBy(ct => ct.SortOrder)
|
||||
.ToArray() ?? Enumerable.Empty<DocumentTypeSort>();
|
||||
target.Compositions = MapNestedCompositions(
|
||||
target.Compositions = MapCompositions(
|
||||
source.ContentTypeComposition,
|
||||
source.ParentId,
|
||||
(referenceByIdModel, compositionType) => new DocumentTypeComposition
|
||||
|
||||
@@ -40,7 +40,7 @@ public class MediaTypeMapDefinition : ContentTypeMapDefinition<IMediaType, Media
|
||||
target.AllowedMediaTypes = source.AllowedContentTypes?.Select(ct =>
|
||||
new MediaTypeSort { MediaType = new ReferenceByIdModel(ct.Key), SortOrder = ct.SortOrder })
|
||||
.ToArray() ?? Enumerable.Empty<MediaTypeSort>();
|
||||
target.Compositions = MapNestedCompositions(
|
||||
target.Compositions = MapCompositions(
|
||||
source.ContentTypeComposition,
|
||||
source.ParentId,
|
||||
(referenceByIdModel, compositionType) => new MediaTypeComposition
|
||||
|
||||
@@ -32,7 +32,7 @@ public class MemberTypeMapDefinition : ContentTypeMapDefinition<IMemberType, Mem
|
||||
target.IsElement = source.IsElement;
|
||||
target.Containers = MapPropertyTypeContainers(source);
|
||||
target.Properties = MapPropertyTypes(source);
|
||||
target.Compositions = MapNestedCompositions(
|
||||
target.Compositions = MapCompositions(
|
||||
source.ContentTypeComposition,
|
||||
source.ParentId,
|
||||
(referenceByIdModel, compositionType) => new MemberTypeComposition
|
||||
|
||||
@@ -36,5 +36,7 @@ public class ConfigureUmbracoBackofficeJsonOptions : IConfigureNamedOptions<Json
|
||||
options.JsonSerializerOptions.Converters.Add(new JsonObjectConverter());
|
||||
|
||||
options.JsonSerializerOptions.TypeInfoResolver = _umbracoJsonTypeInfoResolver;
|
||||
|
||||
options.JsonSerializerOptions.MaxDepth = 64; // Ensures the maximum possible value is used, in particular to support handling as best we can levels of nested blocks.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class ContentVersionMeta
|
||||
|
||||
public int UserId { get; }
|
||||
|
||||
public DateTime VersionDate { get; }
|
||||
public DateTime VersionDate { get; private set; }
|
||||
|
||||
public bool CurrentPublishedVersion { get; }
|
||||
|
||||
@@ -47,5 +47,7 @@ public class ContentVersionMeta
|
||||
|
||||
public string? Username { get; }
|
||||
|
||||
public void SpecifyVersionDateKind(DateTimeKind kind) => VersionDate = DateTime.SpecifyKind(VersionDate, kind);
|
||||
|
||||
public override string ToString() => $"ContentVersionMeta(versionId: {VersionId}, versionDate: {VersionDate:s}";
|
||||
}
|
||||
|
||||
@@ -322,6 +322,15 @@ internal abstract class ContentTypeEditingServiceBase<TContentType, TContentType
|
||||
// get the content type keys we want to use for compositions
|
||||
Guid[] compositionKeys = KeysForCompositionTypes(model, CompositionType.Composition);
|
||||
|
||||
// if the content type keys are already set as compositions, don't perform any additional validation
|
||||
// - this covers an edge case where compositions are configured for a content type before child content types are created
|
||||
if (contentType is not null && contentType.ContentTypeComposition
|
||||
.Select(c => c.Key)
|
||||
.ContainsAll(compositionKeys))
|
||||
{
|
||||
return ContentTypeOperationStatus.Success;
|
||||
}
|
||||
|
||||
// verify that all compositions keys are allowed
|
||||
Guid[] allowedCompositionKeys = _contentTypeService.GetAvailableCompositeContentTypes(contentType, allContentTypeCompositions, isElement: model.IsElement)
|
||||
.Results
|
||||
|
||||
@@ -39,8 +39,11 @@ internal class ContentBaseFactory
|
||||
|
||||
content.CreatorId = nodeDto.UserId ?? Constants.Security.UnknownUserId;
|
||||
content.WriterId = contentVersionDto.UserId ?? Constants.Security.UnknownUserId;
|
||||
content.CreateDate = nodeDto.CreateDate;
|
||||
content.UpdateDate = contentVersionDto.VersionDate;
|
||||
|
||||
// Dates stored in the database are local server time, but for SQL Server, will be considered
|
||||
// as DateTime.Kind = Utc. Fix this so we are consistent when later mapping to DataTimeOffset.
|
||||
content.CreateDate = DateTime.SpecifyKind(nodeDto.CreateDate, DateTimeKind.Local);
|
||||
content.UpdateDate = DateTime.SpecifyKind(contentVersionDto.VersionDate, DateTimeKind.Local);
|
||||
|
||||
content.Published = dto.Published;
|
||||
content.Edited = dto.Edited;
|
||||
@@ -52,7 +55,7 @@ internal class ContentBaseFactory
|
||||
content.PublishedVersionId = publishedVersionDto.Id;
|
||||
if (dto.Published)
|
||||
{
|
||||
content.PublishDate = publishedVersionDto.ContentVersionDto.VersionDate;
|
||||
content.PublishDate = DateTime.SpecifyKind(publishedVersionDto.ContentVersionDto.VersionDate, DateTimeKind.Local);
|
||||
content.PublishName = publishedVersionDto.ContentVersionDto.Text;
|
||||
content.PublisherId = publishedVersionDto.ContentVersionDto.UserId;
|
||||
}
|
||||
@@ -71,7 +74,7 @@ internal class ContentBaseFactory
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds an IMedia item from a dto and content type.
|
||||
/// Builds a Media item from a dto and content type.
|
||||
/// </summary>
|
||||
public static Core.Models.Media BuildEntity(ContentDto dto, IMediaType? contentType)
|
||||
{
|
||||
@@ -97,8 +100,8 @@ internal class ContentBaseFactory
|
||||
|
||||
content.CreatorId = nodeDto.UserId ?? Constants.Security.UnknownUserId;
|
||||
content.WriterId = contentVersionDto.UserId ?? Constants.Security.UnknownUserId;
|
||||
content.CreateDate = nodeDto.CreateDate;
|
||||
content.UpdateDate = contentVersionDto.VersionDate;
|
||||
content.CreateDate = DateTime.SpecifyKind(nodeDto.CreateDate, DateTimeKind.Local);
|
||||
content.UpdateDate = DateTime.SpecifyKind(contentVersionDto.VersionDate, DateTimeKind.Local);
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
content.ResetDirtyProperties(false);
|
||||
@@ -111,7 +114,7 @@ internal class ContentBaseFactory
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds an IMedia item from a dto and content type.
|
||||
/// Builds a Member item from a dto and member type.
|
||||
/// </summary>
|
||||
public static Member BuildEntity(MemberDto dto, IMemberType? contentType)
|
||||
{
|
||||
@@ -126,7 +129,9 @@ internal class ContentBaseFactory
|
||||
|
||||
content.Id = dto.NodeId;
|
||||
content.SecurityStamp = dto.SecurityStampToken;
|
||||
content.EmailConfirmedDate = dto.EmailConfirmedDate;
|
||||
content.EmailConfirmedDate = dto.EmailConfirmedDate.HasValue
|
||||
? DateTime.SpecifyKind(dto.EmailConfirmedDate.Value, DateTimeKind.Local)
|
||||
: null;
|
||||
content.PasswordConfiguration = dto.PasswordConfig;
|
||||
content.Key = nodeDto.UniqueId;
|
||||
content.VersionId = contentVersionDto.Id;
|
||||
@@ -140,14 +145,20 @@ internal class ContentBaseFactory
|
||||
|
||||
content.CreatorId = nodeDto.UserId ?? Constants.Security.UnknownUserId;
|
||||
content.WriterId = contentVersionDto.UserId ?? Constants.Security.UnknownUserId;
|
||||
content.CreateDate = nodeDto.CreateDate;
|
||||
content.UpdateDate = contentVersionDto.VersionDate;
|
||||
content.CreateDate = DateTime.SpecifyKind(nodeDto.CreateDate, DateTimeKind.Local);
|
||||
content.UpdateDate = DateTime.SpecifyKind(contentVersionDto.VersionDate, DateTimeKind.Local);
|
||||
content.FailedPasswordAttempts = dto.FailedPasswordAttempts ?? default;
|
||||
content.IsLockedOut = dto.IsLockedOut;
|
||||
content.IsApproved = dto.IsApproved;
|
||||
content.LastLoginDate = dto.LastLoginDate;
|
||||
content.LastLockoutDate = dto.LastLockoutDate;
|
||||
content.LastPasswordChangeDate = dto.LastPasswordChangeDate;
|
||||
content.LastLockoutDate = dto.LastLockoutDate.HasValue
|
||||
? DateTime.SpecifyKind(dto.LastLockoutDate.Value, DateTimeKind.Local)
|
||||
: null;
|
||||
content.LastLoginDate = dto.LastLoginDate.HasValue
|
||||
? DateTime.SpecifyKind(dto.LastLoginDate.Value, DateTimeKind.Local)
|
||||
: null;
|
||||
content.LastPasswordChangeDate = dto.LastPasswordChangeDate.HasValue
|
||||
? DateTime.SpecifyKind(dto.LastPasswordChangeDate.Value, DateTimeKind.Local)
|
||||
: null;
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
content.ResetDirtyProperties(false);
|
||||
@@ -186,7 +197,7 @@ internal class ContentBaseFactory
|
||||
new ContentScheduleDto
|
||||
{
|
||||
Action = x.Action.ToString(),
|
||||
Date = x.Date,
|
||||
Date = DateTime.SpecifyKind(x.Date, DateTimeKind.Local),
|
||||
NodeId = entity.Id,
|
||||
LanguageId = languageRepository.GetIdByIsoCode(x.Culture, false),
|
||||
Id = x.Id,
|
||||
@@ -261,7 +272,7 @@ internal class ContentBaseFactory
|
||||
UserId = entity.CreatorId,
|
||||
Text = entity.Name,
|
||||
NodeObjectType = objectType,
|
||||
CreateDate = entity.CreateDate,
|
||||
CreateDate = DateTime.SpecifyKind(entity.CreateDate, DateTimeKind.Local),
|
||||
};
|
||||
|
||||
return dto;
|
||||
@@ -275,7 +286,7 @@ internal class ContentBaseFactory
|
||||
{
|
||||
Id = entity.VersionId,
|
||||
NodeId = entity.Id,
|
||||
VersionDate = entity.UpdateDate,
|
||||
VersionDate = DateTime.SpecifyKind(entity.UpdateDate, DateTimeKind.Local),
|
||||
UserId = entity.WriterId,
|
||||
Current = true, // always building the current one
|
||||
Text = entity.Name,
|
||||
|
||||
@@ -39,16 +39,25 @@ internal static class UserFactory
|
||||
user.Language = dto.UserLanguage;
|
||||
user.SecurityStamp = dto.SecurityStampToken;
|
||||
user.FailedPasswordAttempts = dto.FailedLoginAttempts ?? 0;
|
||||
user.LastLockoutDate = dto.LastLockoutDate;
|
||||
user.LastLoginDate = dto.LastLoginDate;
|
||||
user.LastPasswordChangeDate = dto.LastPasswordChangeDate;
|
||||
user.CreateDate = dto.CreateDate;
|
||||
user.UpdateDate = dto.UpdateDate;
|
||||
user.Avatar = dto.Avatar;
|
||||
user.EmailConfirmedDate = dto.EmailConfirmedDate;
|
||||
user.InvitedDate = dto.InvitedDate;
|
||||
user.Kind = (UserKind)dto.Kind;
|
||||
|
||||
// Dates stored in the database are local server time, but for SQL Server, will be considered
|
||||
// as DateTime.Kind = Utc. Fix this so we are consistent when later mapping to DataTimeOffset.
|
||||
user.LastLockoutDate = dto.LastLockoutDate.HasValue
|
||||
? DateTime.SpecifyKind(dto.LastLockoutDate.Value, DateTimeKind.Local)
|
||||
: null;
|
||||
user.LastLoginDate = dto.LastLoginDate.HasValue
|
||||
? DateTime.SpecifyKind(dto.LastLoginDate.Value, DateTimeKind.Local)
|
||||
: null;
|
||||
user.LastPasswordChangeDate = dto.LastPasswordChangeDate.HasValue
|
||||
? DateTime.SpecifyKind(dto.LastPasswordChangeDate.Value, DateTimeKind.Local)
|
||||
: null;
|
||||
user.CreateDate = DateTime.SpecifyKind(dto.CreateDate, DateTimeKind.Local);
|
||||
user.UpdateDate = DateTime.SpecifyKind(dto.UpdateDate, DateTimeKind.Local);
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
user.ResetDirtyProperties(false);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ internal class AuditRepository : EntityRepositoryBase<int, IAuditItem>, IAuditRe
|
||||
|
||||
List<LogDto>? dtos = Database.Fetch<LogDto>(sql);
|
||||
|
||||
return dtos.Select(x => new AuditItem(x.NodeId, Enum<AuditType>.Parse(x.Header), x.UserId ?? Constants.Security.UnknownUserId, x.EntityType, x.Comment, x.Parameters, x.Datestamp)).ToList();
|
||||
return dtos.Select(x => new AuditItem(x.NodeId, Enum<AuditType>.Parse(x.Header), x.UserId ?? Constants.Security.UnknownUserId, x.EntityType, x.Comment, x.Parameters, DateTime.SpecifyKind(x.Datestamp, DateTimeKind.Local))).ToList();
|
||||
}
|
||||
|
||||
public void CleanLogs(int maximumAgeOfLogsInMinutes)
|
||||
@@ -104,12 +104,12 @@ internal class AuditRepository : EntityRepositoryBase<int, IAuditItem>, IAuditRe
|
||||
totalRecords = page.TotalItems;
|
||||
|
||||
var items = page.Items.Select(
|
||||
dto => new AuditItem(dto.NodeId, Enum<AuditType>.ParseOrNull(dto.Header) ?? AuditType.Custom, dto.UserId ?? Constants.Security.UnknownUserId, dto.EntityType, dto.Comment, dto.Parameters, dto.Datestamp)).ToList();
|
||||
dto => new AuditItem(dto.NodeId, Enum<AuditType>.ParseOrNull(dto.Header) ?? AuditType.Custom, dto.UserId ?? Constants.Security.UnknownUserId, dto.EntityType, dto.Comment, dto.Parameters, DateTime.SpecifyKind(dto.Datestamp, DateTimeKind.Local))).ToList();
|
||||
|
||||
// map the DateStamp
|
||||
for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
items[i].CreateDate = page.Items[i].Datestamp;
|
||||
items[i].CreateDate = DateTime.SpecifyKind(page.Items[i].Datestamp, DateTimeKind.Local);
|
||||
}
|
||||
|
||||
return items;
|
||||
@@ -149,7 +149,7 @@ internal class AuditRepository : EntityRepositoryBase<int, IAuditItem>, IAuditRe
|
||||
LogDto? dto = Database.First<LogDto>(sql);
|
||||
return dto == null
|
||||
? null
|
||||
: new AuditItem(dto.NodeId, Enum<AuditType>.Parse(dto.Header), dto.UserId ?? Constants.Security.UnknownUserId, dto.EntityType, dto.Comment, dto.Parameters, dto.Datestamp);
|
||||
: new AuditItem(dto.NodeId, Enum<AuditType>.Parse(dto.Header), dto.UserId ?? Constants.Security.UnknownUserId, dto.EntityType, dto.Comment, dto.Parameters, DateTime.SpecifyKind(dto.Datestamp, DateTimeKind.Local));
|
||||
}
|
||||
|
||||
protected override IEnumerable<IAuditItem> PerformGetAll(params int[]? ids) => throw new NotImplementedException();
|
||||
@@ -162,7 +162,7 @@ internal class AuditRepository : EntityRepositoryBase<int, IAuditItem>, IAuditRe
|
||||
|
||||
List<LogDto>? dtos = Database.Fetch<LogDto>(sql);
|
||||
|
||||
return dtos.Select(x => new AuditItem(x.NodeId, Enum<AuditType>.Parse(x.Header), x.UserId ?? Constants.Security.UnknownUserId, x.EntityType, x.Comment, x.Parameters, x.Datestamp)).ToList();
|
||||
return dtos.Select(x => new AuditItem(x.NodeId, Enum<AuditType>.Parse(x.Header), x.UserId ?? Constants.Security.UnknownUserId, x.EntityType, x.Comment, x.Parameters, DateTime.SpecifyKind(x.Datestamp, DateTimeKind.Local))).ToList();
|
||||
}
|
||||
|
||||
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
|
||||
|
||||
@@ -400,15 +400,17 @@ public class DocumentRepository : ContentRepositoryBase<int, IContent, DocumentR
|
||||
{
|
||||
foreach (ContentVariation v in contentVariation)
|
||||
{
|
||||
content.SetCultureInfo(v.Culture, v.Name, v.Date);
|
||||
content.SetCultureInfo(v.Culture, v.Name, DateTime.SpecifyKind(v.Date, DateTimeKind.Local));
|
||||
}
|
||||
}
|
||||
|
||||
// Dates stored in the database are local server time, but for SQL Server, will be considered
|
||||
// as DateTime.Kind = Utc. Fix this so we are consistent when later mapping to DataTimeOffset.
|
||||
if (content.PublishedState is PublishedState.Published && content.PublishedVersionId > 0 && contentVariations.TryGetValue(content.PublishedVersionId, out contentVariation))
|
||||
{
|
||||
foreach (ContentVariation v in contentVariation)
|
||||
{
|
||||
content.SetPublishInfo(v.Culture, v.Name, v.Date);
|
||||
content.SetPublishInfo(v.Culture, v.Name, DateTime.SpecifyKind(v.Date, DateTimeKind.Local));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -1,3 +1,4 @@
|
||||
using System.Data;
|
||||
using NPoco;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -98,6 +99,16 @@ internal class DocumentVersionRepository : IDocumentVersionRepository
|
||||
Page<ContentVersionMeta>? page =
|
||||
_scopeAccessor.AmbientScope?.Database.Page<ContentVersionMeta>(pageIndex + 1, pageSize, query);
|
||||
|
||||
// Dates stored in the database are local server time, but for SQL Server, will be considered
|
||||
// as DateTime.Kind = Utc. Fix this so we are consistent when later mapping to DataTimeOffset.
|
||||
if (page is not null)
|
||||
{
|
||||
foreach (ContentVersionMeta item in page.Items)
|
||||
{
|
||||
item.SpecifyVersionDateKind(DateTimeKind.Local);
|
||||
}
|
||||
}
|
||||
|
||||
totalRecords = page?.TotalItems ?? 0;
|
||||
|
||||
return page?.Items;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Extensions.Caching.Hybrid;
|
||||
using Microsoft.Extensions.Caching.Hybrid;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -115,12 +115,10 @@ internal sealed class DocumentCacheService : IDocumentCacheService
|
||||
// When unpublishing a node, a payload with RefreshBranch is published, so we don't have to worry about this.
|
||||
// Similarly, when a branch is published, next time the content is requested, the parent will be published,
|
||||
// this works because we don't cache null values.
|
||||
if (preview is false && contentCacheNode is not null)
|
||||
if (preview is false && contentCacheNode is not null && HasPublishedAncestorPath(contentCacheNode.Key) is false)
|
||||
{
|
||||
if (HasPublishedAncestorPath(contentCacheNode.Key) is false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// Careful not to early return here. We need to complete the scope even if returning null.
|
||||
contentCacheNode = null;
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
|
||||
+4
-4
@@ -88,7 +88,7 @@
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"typescript-json-schema": "^0.65.1",
|
||||
"vite": "^6.2.5",
|
||||
"vite": "^6.2.6",
|
||||
"vite-plugin-static-copy": "^2.2.0",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"web-component-analyzer": "^2.0.0"
|
||||
@@ -16880,9 +16880,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.2.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz",
|
||||
"integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==",
|
||||
"version": "6.2.6",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz",
|
||||
"integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -275,7 +275,7 @@
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"typescript-json-schema": "^0.65.1",
|
||||
"vite": "^6.2.5",
|
||||
"vite": "^6.2.6",
|
||||
"vite-plugin-static-copy": "^2.2.0",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"web-component-analyzer": "^2.0.0"
|
||||
|
||||
+8
@@ -17,6 +17,8 @@ import {
|
||||
UmbRequestReloadStructureForEntityEvent,
|
||||
} from '@umbraco-cms/backoffice/entity-action';
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UMB_DOCUMENT_TYPE_ENTITY_TYPE } from '@umbraco-cms/backoffice/document-type';
|
||||
import { CompositionTypeModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
export interface UmbContentTypeWorkspaceContextArgs extends UmbEntityDetailWorkspaceContextArgs {}
|
||||
@@ -95,6 +97,12 @@ export abstract class UmbContentTypeWorkspaceContextBase<
|
||||
this.setUnique(data.unique);
|
||||
this.setIsNew(true);
|
||||
this._data.setPersisted(data);
|
||||
|
||||
if (!args.preset && args.parent.entityType === UMB_DOCUMENT_TYPE_ENTITY_TYPE && args.parent.unique) {
|
||||
this.setCompositions([
|
||||
{ contentType: { unique: args.parent.unique }, compositionType: CompositionTypeModel.INHERITANCE },
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
this.loading.removeState(LOADING_STATE_UNIQUE);
|
||||
|
||||
+6
-1
@@ -10,7 +10,12 @@ export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> =
|
||||
alias: 'Umb.EntityAction.DocumentType.Create',
|
||||
name: 'Create Document Type Entity Action',
|
||||
weight: 1200,
|
||||
forEntityTypes: [UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE, UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE],
|
||||
api: () => import('./create.action.js'),
|
||||
forEntityTypes: [
|
||||
UMB_DOCUMENT_TYPE_ENTITY_TYPE,
|
||||
UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE,
|
||||
UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE,
|
||||
],
|
||||
meta: {
|
||||
icon: 'icon-add',
|
||||
label: '#actions_create',
|
||||
|
||||
+10
-5
@@ -1,6 +1,8 @@
|
||||
import { UMB_DOCUMENT_TYPE_FOLDER_REPOSITORY_ALIAS } from '../../../tree/index.js';
|
||||
import {
|
||||
UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PATH_PATTERN,
|
||||
UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_ELEMENT,
|
||||
UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_TEMPLATE,
|
||||
type UmbCreateDocumentTypeWorkspacePresetType,
|
||||
} from '../../../paths.js';
|
||||
import type { UmbDocumentTypeEntityTypeUnion } from '../../../entity.js';
|
||||
@@ -9,8 +11,10 @@ import { html, customElement, map } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbCreateFolderEntityAction } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
const CREATE_FOLDER_PRESET = 'folder';
|
||||
|
||||
// Include the types from the DocumentTypeWorkspacePresetType + folder.
|
||||
type OptionsPresetType = UmbCreateDocumentTypeWorkspacePresetType | 'folder' | null;
|
||||
type OptionsPresetType = UmbCreateDocumentTypeWorkspacePresetType | typeof CREATE_FOLDER_PRESET | null;
|
||||
|
||||
/** @deprecated No longer used internally. This will be removed in Umbraco 17. [LK] */
|
||||
@customElement('umb-document-type-create-options-modal')
|
||||
@@ -30,13 +34,13 @@ export class UmbDataTypeCreateOptionsModalElement extends UmbModalBaseElement<Um
|
||||
icon: 'icon-document',
|
||||
},
|
||||
{
|
||||
preset: 'template',
|
||||
preset: UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_TEMPLATE,
|
||||
label: this.localize.term('create_documentTypeWithTemplate'),
|
||||
description: this.localize.term('create_documentTypeWithTemplateDescription'),
|
||||
icon: 'icon-document-html',
|
||||
},
|
||||
{
|
||||
preset: 'element',
|
||||
preset: UMB_CREATE_DOCUMENT_TYPE_WORKSPACE_PRESET_ELEMENT,
|
||||
label: this.localize.term('create_elementType'),
|
||||
description: this.localize.term('create_elementTypeDescription'),
|
||||
icon: 'icon-plugin',
|
||||
@@ -49,7 +53,7 @@ export class UmbDataTypeCreateOptionsModalElement extends UmbModalBaseElement<Um
|
||||
// icon: 'icon-plugin',
|
||||
// },
|
||||
{
|
||||
preset: 'folder',
|
||||
preset: CREATE_FOLDER_PRESET,
|
||||
label: this.localize.term('create_folder'),
|
||||
description: this.localize.term('create_folderDescription'),
|
||||
icon: 'icon-folder',
|
||||
@@ -67,9 +71,10 @@ export class UmbDataTypeCreateOptionsModalElement extends UmbModalBaseElement<Um
|
||||
meta: { icon: '', label: '', folderRepositoryAlias: UMB_DOCUMENT_TYPE_FOLDER_REPOSITORY_ALIAS },
|
||||
});
|
||||
}
|
||||
|
||||
async #onClick(presetAlias: OptionsPresetType) {
|
||||
switch (presetAlias) {
|
||||
case 'folder': {
|
||||
case CREATE_FOLDER_PRESET: {
|
||||
try {
|
||||
await this.#createFolderAction?.execute();
|
||||
this._submitModal();
|
||||
|
||||
+16
-3
@@ -9,18 +9,19 @@ import type { UmbDocumentTypeDetailModel } from '../../types.js';
|
||||
import { UMB_DOCUMENT_TYPE_ENTITY_TYPE, UMB_DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS } from '../../constants.js';
|
||||
import { UmbDocumentTypeWorkspaceEditorElement } from './document-type-workspace-editor.element.js';
|
||||
import { UMB_DOCUMENT_TYPE_WORKSPACE_ALIAS } from './constants.js';
|
||||
import { CompositionTypeModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import { UmbContentTypeWorkspaceContextBase } from '@umbraco-cms/backoffice/content-type';
|
||||
import { UmbTemplateDetailRepository } from '@umbraco-cms/backoffice/template';
|
||||
import {
|
||||
UmbWorkspaceIsNewRedirectController,
|
||||
UmbWorkspaceIsNewRedirectControllerAlias,
|
||||
} from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbContentTypeSortModel, UmbContentTypeWorkspaceContext } from '@umbraco-cms/backoffice/content-type';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import type { UmbPathPatternTypeAsEncodedParamsType } from '@umbraco-cms/backoffice/router';
|
||||
import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models';
|
||||
import type { UmbRoutableWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbPathPatternTypeAsEncodedParamsType } from '@umbraco-cms/backoffice/router';
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UmbTemplateDetailRepository } from '@umbraco-cms/backoffice/template';
|
||||
|
||||
type DetailModelType = UmbDocumentTypeDetailModel;
|
||||
export class UmbDocumentTypeWorkspaceContext
|
||||
@@ -148,6 +149,18 @@ export class UmbDocumentTypeWorkspaceContext
|
||||
break;
|
||||
}
|
||||
|
||||
if (parent.unique && parent.entityType === UMB_DOCUMENT_TYPE_ENTITY_TYPE) {
|
||||
preset = {
|
||||
...preset,
|
||||
compositions: [
|
||||
{
|
||||
contentType: { unique: parent.unique },
|
||||
compositionType: CompositionTypeModel.INHERITANCE,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
this.createScaffold({ parent, preset });
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -11,7 +11,7 @@
|
||||
"@umbraco-cms/backoffice": "15.3.0",
|
||||
"msw": "^2.7.0",
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^6.2.5",
|
||||
"vite": "^6.2.6",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4292,9 +4292,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.2.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz",
|
||||
"integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==",
|
||||
"version": "6.2.6",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz",
|
||||
"integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"@umbraco-cms/backoffice": "15.3.0",
|
||||
"msw": "^2.7.0",
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^6.2.5",
|
||||
"vite": "^6.2.6",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
"msw": {
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
import {ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers';
|
||||
|
||||
// Content Name
|
||||
const contentName = 'ContentName';
|
||||
|
||||
// Document Type
|
||||
const documentTypeName = 'DocumentTypeName';
|
||||
let documentTypeId = null;
|
||||
const documentTypeGroupName = 'DocumentGroup';
|
||||
|
||||
// Block Grid
|
||||
const blockGridName = 'BlockGridName';
|
||||
let blockGridId = null;
|
||||
|
||||
// Element Type
|
||||
const blockName = 'BlockName';
|
||||
let elementTypeId = null;
|
||||
const elementGroupName = 'ElementGroup';
|
||||
|
||||
// Property Editor
|
||||
const propertyEditorName = 'ProperyEditorInBlockName';
|
||||
let propertyEditorId = null;
|
||||
const optionValues = ['testOption1', 'testOption2'];
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockGridName);
|
||||
});
|
||||
|
||||
test('can not publish a block grid with a mandatory radiobox without a value', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
propertyEditorId = await umbracoApi.dataType.createRadioboxDataType(propertyEditorName, optionValues);
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementType(blockName, elementGroupName, propertyEditorName, propertyEditorId, true);
|
||||
blockGridId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridName, elementTypeId, true);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridName, blockGridId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
// Do not select any radiobox values and the validation error appears
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue);
|
||||
// Select a radiobox value and the validation error disappears
|
||||
await umbracoUi.content.chooseRadioboxOption(optionValues[0]);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue, false);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
});
|
||||
|
||||
test('can not publish a block grid with a mandatory checkbox list without a value', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
propertyEditorId = await umbracoApi.dataType.createCheckboxListDataType(propertyEditorName, optionValues);
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementType(blockName, elementGroupName, propertyEditorName, propertyEditorId, true);
|
||||
blockGridId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridName, elementTypeId, true);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridName, blockGridId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
// Do not select any checkbox list values and the validation error appears
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue);
|
||||
// Select a checkbox list value and the validation error disappears
|
||||
await umbracoUi.content.chooseCheckboxListOption(optionValues[0]);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue, false);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
});
|
||||
|
||||
test('can not publish a block grid with a mandatory dropdown without a value', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
propertyEditorId = await umbracoApi.dataType.createDropdownDataType(propertyEditorName, false, optionValues);
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementType(blockName, elementGroupName, propertyEditorName, propertyEditorId, true);
|
||||
blockGridId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridName, elementTypeId, true);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridName, blockGridId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
// Do not select any dropdown values and the validation error appears
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue);
|
||||
// Select a dropdown value and the validation error disappears
|
||||
await umbracoUi.content.chooseDropdownOption([optionValues[0]]);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue, false);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
});
|
||||
+11
-26
@@ -32,9 +32,10 @@ test.beforeEach(async ({umbracoApi}) => {
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.language.ensureIsoCodeNotExists('da');
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockGridName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
});
|
||||
|
||||
test('invariant document type with invariant block grid with invariant block with an invariant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
@@ -63,34 +64,25 @@ test('invariant document type with invariant block grid with invariant block wit
|
||||
await umbracoUi.content.doesPropertyContainValue(textStringName, textStringText);
|
||||
});
|
||||
|
||||
test('invariant document type with invariant block grid with variant block with an invariant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
test('can not create unsupported invariant document type with invariant block grid with variant block with an invariant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementTypeWithVaryByCulture(blockName, elementGroupName, textStringName, textStringDataTypeId, true, false);
|
||||
blockGridId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridName, elementTypeId, true);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockGridName, blockGridId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
await umbracoUi.content.enterTextstring(textStringText);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.unsupportInvariantContentItemWithVariantBlocks);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
|
||||
await umbracoUi.reloadPage();
|
||||
await umbracoUi.content.goToBlockGridBlockWithName(documentTypeGroupName, blockGridName, blockName);
|
||||
await umbracoUi.content.doesPropertyContainValue(textStringName, textStringText);
|
||||
await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished);
|
||||
});
|
||||
|
||||
// Remove fixme when this test works. Currently, the textstring value is not saved when saving / publishing the document
|
||||
test.fixme('invariant document type with invariant block grid with variant block with an variant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
test('can not create unsupported invariant document type with invariant block grid with variant block with an variant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementTypeWithVaryByCulture(blockName, elementGroupName, textStringName, textStringDataTypeId, true, true);
|
||||
blockGridId = await umbracoApi.dataType.createBlockGridWithABlockAndAllowAtRoot(blockGridName, elementTypeId, true);
|
||||
@@ -98,22 +90,15 @@ test.fixme('invariant document type with invariant block grid with variant block
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName)
|
||||
await umbracoUi.content.enterTextstring(textStringText);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.unsupportInvariantContentItemWithVariantBlocks);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
|
||||
await umbracoUi.reloadPage();
|
||||
await umbracoUi.content.goToBlockGridBlockWithName(documentTypeGroupName, blockGridName, blockName);
|
||||
await umbracoUi.content.doesPropertyContainValue(textStringName, textStringText);
|
||||
await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished);
|
||||
});
|
||||
|
||||
test('variant document type with variant block grid with variant block with an variant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
@@ -195,4 +180,4 @@ test('variant document type with invariant block grid with variant block with an
|
||||
await umbracoUi.reloadPage();
|
||||
await umbracoUi.content.goToBlockGridBlockWithName(documentTypeGroupName, blockGridName, blockName);
|
||||
await umbracoUi.content.doesPropertyContainValue(textStringName, textStringText);
|
||||
});
|
||||
});
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
import {ConstantHelper, NotificationConstantHelper, test} from '@umbraco/playwright-testhelpers';
|
||||
|
||||
// Content Name
|
||||
const contentName = 'ContentName';
|
||||
|
||||
// Document Type
|
||||
const documentTypeName = 'DocumentTypeName';
|
||||
let documentTypeId = null;
|
||||
const documentTypeGroupName = 'DocumentGroup';
|
||||
|
||||
// Block List
|
||||
const blockListName = 'BlockListName';
|
||||
let blockListId = null;
|
||||
|
||||
// Element Type
|
||||
const blockName = 'BlockName';
|
||||
let elementTypeId = null;
|
||||
const elementGroupName = 'ElementGroup';
|
||||
|
||||
// Property Editor
|
||||
const propertyEditorName = 'ProperyEditorInBlockName';
|
||||
let propertyEditorId = null;
|
||||
const optionValues = ['testOption1', 'testOption2'];
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockListName);
|
||||
});
|
||||
|
||||
test('can not publish a block list with a mandatory radiobox without a value', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
propertyEditorId = await umbracoApi.dataType.createRadioboxDataType(propertyEditorName, optionValues);
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementType(blockName, elementGroupName, propertyEditorName, propertyEditorId, true);
|
||||
blockListId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListName, elementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockListName, blockListId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
// Do not select any radiobox values and the validation error appears
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue);
|
||||
// Select a radiobox value and the validation error disappears
|
||||
await umbracoUi.content.chooseRadioboxOption(optionValues[0]);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue, false);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
});
|
||||
|
||||
test('can not publish a block list with a mandatory checkbox list without a value', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
propertyEditorId = await umbracoApi.dataType.createCheckboxListDataType(propertyEditorName, optionValues);
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementType(blockName, elementGroupName, propertyEditorName, propertyEditorId, true);
|
||||
blockListId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListName, elementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockListName, blockListId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
// Do not select any checkbox list values and the validation error appears
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue);
|
||||
// Select a checkbox list value and the validation error disappears
|
||||
await umbracoUi.content.chooseCheckboxListOption(optionValues[0]);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue, false);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
});
|
||||
|
||||
test('can not publish a block list with a mandatory dropdown without a value', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
propertyEditorId = await umbracoApi.dataType.createDropdownDataType(propertyEditorName, false, optionValues);
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementType(blockName, elementGroupName, propertyEditorName, propertyEditorId, true);
|
||||
blockListId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListName, elementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockListName, blockListId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
// Do not select any dropdown values and the validation error appears
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue);
|
||||
// Select a dropdown value and the validation error disappears
|
||||
await umbracoUi.content.chooseDropdownOption([optionValues[0]]);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue, false);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
});
|
||||
+10
-26
@@ -32,9 +32,10 @@ test.beforeEach(async ({umbracoApi}) => {
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.language.ensureIsoCodeNotExists('da');
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(blockListName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
});
|
||||
|
||||
test('invariant document type with invariant block list with invariant block with an invariant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
@@ -63,57 +64,40 @@ test('invariant document type with invariant block list with invariant block wit
|
||||
await umbracoUi.content.doesPropertyContainValue(textStringName, textStringText);
|
||||
});
|
||||
|
||||
test('invariant document type with invariant block list with variant block with an invariant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
test('can not create unsupported invariant document type with invariant block list with variant block with an invariant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementTypeWithVaryByCulture(blockName, elementGroupName, textStringName, textStringDataTypeId, true, false);
|
||||
blockListId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListName, elementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockListName, blockListId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
await umbracoUi.content.enterTextstring(textStringText);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.unsupportInvariantContentItemWithVariantBlocks);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
|
||||
await umbracoUi.reloadPage();
|
||||
await umbracoUi.content.goToBlockListBlockWithName(documentTypeGroupName, blockListName, blockName);
|
||||
await umbracoUi.content.doesPropertyContainValue(textStringName, textStringText);
|
||||
await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished);
|
||||
});
|
||||
|
||||
// Remove fixme when this test works. Currently the textstring value is is not saved when saving / publishing the document
|
||||
test.fixme('invariant document type with invariant block list with variant block with an variant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
test('can not create unsupported invariant document type with invariant block list with variant block with an variant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementTypeWithVaryByCulture(blockName, elementGroupName, textStringName, textStringDataTypeId, true, true);
|
||||
blockListId = await umbracoApi.dataType.createBlockListDataTypeWithABlock(blockListName, elementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, blockListName, blockListId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickAddBlockElementButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName)
|
||||
await umbracoUi.content.enterTextstring(textStringText);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.unsupportInvariantContentItemWithVariantBlocks);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
|
||||
await umbracoUi.reloadPage();
|
||||
await umbracoUi.content.goToBlockListBlockWithName(documentTypeGroupName, blockListName, blockName);
|
||||
await umbracoUi.content.doesPropertyContainValue(textStringName, textStringText);
|
||||
await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished);
|
||||
});
|
||||
|
||||
test('variant document type with variant block list with variant block with an variant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
|
||||
+37
-10
@@ -1,19 +1,22 @@
|
||||
import {ConstantHelper, test, AliasHelper} from '@umbraco/playwright-testhelpers';
|
||||
import {ConstantHelper, test, AliasHelper, NotificationConstantHelper} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
const contentName = 'TestContent';
|
||||
const documentTypeName = 'TestDocumentTypeForContent';
|
||||
const dataTypeName = 'Checkbox list';
|
||||
const customDataTypeName = 'CustomCheckboxList';
|
||||
|
||||
test.beforeEach(async ({umbracoApi, umbracoUi}) => {
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
await umbracoUi.goToBackOffice();
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
});
|
||||
|
||||
test('can create content with the checkbox list data type', async ({umbracoApi, umbracoUi}) => {
|
||||
@@ -31,7 +34,7 @@ test('can create content with the checkbox list data type', async ({umbracoApi,
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.created);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe(expectedState);
|
||||
@@ -51,8 +54,8 @@ test('can publish content with the checkbox list data type', async ({umbracoApi,
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationsHaveCount(2);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe(expectedState);
|
||||
expect(contentData.values).toEqual([]);
|
||||
@@ -60,7 +63,6 @@ test('can publish content with the checkbox list data type', async ({umbracoApi,
|
||||
|
||||
test('can create content with the custom checkbox list data type', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const customDataTypeName = 'CustomCheckboxList';
|
||||
const optionValues = ['testOption1', 'testOption2'];
|
||||
const customDataTypeId = await umbracoApi.dataType.createCheckboxListDataType(customDataTypeName, optionValues);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
@@ -73,13 +75,38 @@ test('can create content with the custom checkbox list data type', async ({umbra
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationsHaveCount(2);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].alias).toEqual(AliasHelper.toAlias(customDataTypeName));
|
||||
expect(contentData.values[0].value).toEqual([optionValues[0]]);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
});
|
||||
|
||||
test('can not publish a mandatory checkbox list with an empty value', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const optionValues = ['testOption1', 'testOption2'];
|
||||
const customDataTypeId = await umbracoApi.dataType.createCheckboxListDataType(customDataTypeName, optionValues);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId, 'Test Group', false, false, true);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
// Do not select any checkbox list values and the validation error appears
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished);
|
||||
// Select a checkbox list value and the validation error disappears
|
||||
await umbracoUi.content.chooseCheckboxListOption(optionValues[0]);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue, false);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].alias).toEqual(AliasHelper.toAlias(customDataTypeName));
|
||||
expect(contentData.values[0].value).toEqual([optionValues[0]]);
|
||||
});
|
||||
+108
-82
@@ -1,90 +1,116 @@
|
||||
import {ConstantHelper, test, AliasHelper} from '@umbraco/playwright-testhelpers';
|
||||
import {ConstantHelper, test, AliasHelper, NotificationConstantHelper} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
const contentName = 'TestContent';
|
||||
const documentTypeName = 'TestDocumentTypeForContent';
|
||||
|
||||
const dataTypeNames = ['Dropdown', 'Dropdown multiple'];
|
||||
const customDataTypeName = 'CustomDropdown';
|
||||
|
||||
test.beforeEach(async ({umbracoApi, umbracoUi}) => {
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
await umbracoUi.goToBackOffice();
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
});
|
||||
|
||||
for (const dataTypeName of dataTypeNames) {
|
||||
test.describe(`${dataTypeName} tests`, () => {
|
||||
test.beforeEach(async ({umbracoApi, umbracoUi}) => {
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoUi.goToBackOffice();
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
});
|
||||
|
||||
test(`can create content with the ${dataTypeName} data type`, async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const expectedState = 'Draft';
|
||||
const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName);
|
||||
await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, dataTypeName, dataTypeData.id);
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickActionsMenuAtRoot();
|
||||
await umbracoUi.content.clickCreateButton();
|
||||
await umbracoUi.content.chooseDocumentType(documentTypeName);
|
||||
await umbracoUi.content.enterContentName(contentName);
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe(expectedState);
|
||||
expect(contentData.values).toEqual([]);
|
||||
});
|
||||
|
||||
test(`can publish content with the ${dataTypeName} data type`, async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const expectedState = 'Published';
|
||||
const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, dataTypeName, dataTypeData.id);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationsHaveCount(2);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe(expectedState);
|
||||
expect(contentData.values).toEqual([]);
|
||||
});
|
||||
|
||||
test(`can create content with the custom ${dataTypeName} data type`, async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const customDataTypeName = 'CustomDropdown';
|
||||
const optionValues = ['testOption1', 'testOption2', 'testOption3'];
|
||||
const selectedOptions = dataTypeName === 'Dropdown' ? [optionValues[0]] : optionValues;
|
||||
const isMultiple = dataTypeName === 'Dropdown' ? false : true;
|
||||
const customDataTypeId = await umbracoApi.dataType.createDropdownDataType(customDataTypeName, isMultiple, optionValues);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.chooseDropdownOption(selectedOptions);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationsHaveCount(2);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].alias).toEqual(AliasHelper.toAlias(customDataTypeName));
|
||||
expect(contentData.values[0].value).toEqual(selectedOptions);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
});
|
||||
test(`can create content with the ${dataTypeName} data type`, async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const expectedState = 'Draft';
|
||||
const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName);
|
||||
await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, dataTypeName, dataTypeData.id);
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickActionsMenuAtRoot();
|
||||
await umbracoUi.content.clickCreateButton();
|
||||
await umbracoUi.content.chooseDocumentType(documentTypeName);
|
||||
await umbracoUi.content.enterContentName(contentName);
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.created);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe(expectedState);
|
||||
expect(contentData.values).toEqual([]);
|
||||
});
|
||||
|
||||
test(`can publish content with the ${dataTypeName} data type`, async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const expectedState = 'Published';
|
||||
const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, dataTypeName, dataTypeData.id);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe(expectedState);
|
||||
expect(contentData.values).toEqual([]);
|
||||
});
|
||||
|
||||
test(`can create content with the custom ${dataTypeName} data type`, async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const optionValues = ['testOption1', 'testOption2', 'testOption3'];
|
||||
const selectedOptions = dataTypeName === 'Dropdown' ? [optionValues[0]] : optionValues;
|
||||
const isMultiple = dataTypeName === 'Dropdown' ? false : true;
|
||||
const customDataTypeId = await umbracoApi.dataType.createDropdownDataType(customDataTypeName, isMultiple, optionValues);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.chooseDropdownOption(selectedOptions);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].alias).toEqual(AliasHelper.toAlias(customDataTypeName));
|
||||
expect(contentData.values[0].value).toEqual(selectedOptions);
|
||||
});
|
||||
}
|
||||
|
||||
test('can not publish a mandatory dropdown with an empty value', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const optionValues = ['testOption1', 'testOption2', 'testOption3'];
|
||||
const customDataTypeId = await umbracoApi.dataType.createDropdownDataType(customDataTypeName, false, optionValues);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId, 'Test Group', false, false, true);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
// Do not select any dropdown values and the validation error appears
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished);
|
||||
// Select a dropdown value and the validation error disappears
|
||||
await umbracoUi.content.chooseDropdownOption([optionValues[0]]);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue, false);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].alias).toEqual(AliasHelper.toAlias(customDataTypeName));
|
||||
expect(contentData.values[0].value).toEqual([optionValues[0]]);
|
||||
});
|
||||
+37
-5
@@ -1,4 +1,4 @@
|
||||
import {ConstantHelper, test, AliasHelper} from '@umbraco/playwright-testhelpers';
|
||||
import {ConstantHelper, test, AliasHelper, NotificationConstantHelper} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
const dataTypeName = 'Media Picker';
|
||||
@@ -8,7 +8,7 @@ const mediaFileName = 'TestMediaFileForContent';
|
||||
const mediaTypeName = 'File';
|
||||
let mediaFileId = '';
|
||||
|
||||
test.beforeEach(async ({umbracoApi, umbracoUi}) => {
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.media.ensureNameNotExists(mediaFileName);
|
||||
@@ -39,7 +39,7 @@ test('can create content with the media picker data type', {tag: '@smoke'}, asyn
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.created);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe(expectedState);
|
||||
@@ -68,7 +68,8 @@ test('can publish content with the media picker data type', async ({umbracoApi,
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationsHaveCount(2);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe(expectedState);
|
||||
@@ -93,7 +94,7 @@ test('can remove a media picker in the content', async ({umbracoApi, umbracoUi})
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values).toEqual([]);
|
||||
@@ -128,3 +129,34 @@ test('can limit the media picker in the content by setting the start node', asyn
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
});
|
||||
|
||||
test('can not publish a mandatory media picker with an empty value', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, dataTypeName, dataTypeData.id, 'Test Group', false, false, true);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
// Do not pick any media and the validation error appears
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished);
|
||||
// Pick a media value and the validation error disappears
|
||||
await umbracoUi.content.clickChooseButtonAndSelectMediaWithName(mediaFileName);
|
||||
await umbracoUi.content.clickChooseModalButton();
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue, false);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].alias).toEqual(AliasHelper.toAlias(dataTypeName));
|
||||
expect(contentData.values[0].value[0].mediaKey).toEqual(mediaFileId);
|
||||
expect(contentData.values[0].value[0].mediaTypeAlias).toEqual(mediaTypeName);
|
||||
expect(contentData.values[0].value[0].focalPoint).toBeNull();
|
||||
expect(contentData.values[0].value[0].crops).toEqual([]);
|
||||
});
|
||||
+36
-9
@@ -1,18 +1,22 @@
|
||||
import {ConstantHelper, test, AliasHelper} from '@umbraco/playwright-testhelpers';
|
||||
import {ConstantHelper, test, AliasHelper, NotificationConstantHelper} from '@umbraco/playwright-testhelpers';
|
||||
import {expect} from "@playwright/test";
|
||||
|
||||
const contentName = 'TestContent';
|
||||
const documentTypeName = 'TestDocumentTypeForContent';
|
||||
const dataTypeName = 'Radiobox';
|
||||
const customDataTypeName = 'CustomRadiobox';
|
||||
const optionValues = ['testOption1', 'testOption2'];
|
||||
|
||||
test.beforeEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
});
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
});
|
||||
|
||||
test('can create content with the radiobox data type', async ({umbracoApi, umbracoUi}) => {
|
||||
@@ -31,7 +35,7 @@ test('can create content with the radiobox data type', async ({umbracoApi, umbra
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.created);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe(expectedState);
|
||||
@@ -52,7 +56,8 @@ test('can publish content with the radiobox data type', async ({umbracoApi, umbr
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationsHaveCount(2);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.variants[0].state).toBe(expectedState);
|
||||
@@ -61,8 +66,6 @@ test('can publish content with the radiobox data type', async ({umbracoApi, umbr
|
||||
|
||||
test('can create content with the custom radiobox data type', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const customDataTypeName = 'CustomRadiobox';
|
||||
const optionValues = ['testOption1', 'testOption2'];
|
||||
const customDataTypeId = await umbracoApi.dataType.createRadioboxDataType(customDataTypeName, optionValues);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
@@ -75,13 +78,37 @@ test('can create content with the custom radiobox data type', async ({umbracoApi
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isSuccessNotificationVisible();
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].alias).toEqual(AliasHelper.toAlias(customDataTypeName));
|
||||
expect(contentData.values[0].value).toEqual(optionValues[0]);
|
||||
|
||||
// Clean
|
||||
await umbracoApi.dataType.ensureNameNotExists(customDataTypeName);
|
||||
});
|
||||
|
||||
test('can not publish mandatory radiobox with an empty value', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const customDataTypeId = await umbracoApi.dataType.createRadioboxDataType(customDataTypeName, optionValues);
|
||||
const documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, customDataTypeName, customDataTypeId, 'Test Group', false, false, true);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
// Do not select any radiobox values and the validation error appears
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished);
|
||||
// Select a radiobox value and the validation error disappears
|
||||
await umbracoUi.content.chooseRadioboxOption(optionValues[0]);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.emptyValue, false);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values[0].alias).toEqual(AliasHelper.toAlias(customDataTypeName));
|
||||
expect(contentData.values[0].value).toEqual(optionValues[0]);
|
||||
});
|
||||
+1
-2
@@ -81,5 +81,4 @@ test('can remove a tag in the content', async ({umbracoApi, umbracoUi}) => {
|
||||
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
|
||||
const contentData = await umbracoApi.document.getByName(contentName);
|
||||
expect(contentData.values).toEqual([]);
|
||||
});
|
||||
|
||||
});
|
||||
+10
-25
@@ -32,9 +32,10 @@ test.beforeEach(async ({umbracoApi}) => {
|
||||
|
||||
test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.language.ensureIsoCodeNotExists('da');
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(blockName);
|
||||
await umbracoApi.dataType.ensureNameNotExists(tipTapName);
|
||||
await umbracoApi.document.ensureNameNotExists(contentName);
|
||||
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
|
||||
});
|
||||
|
||||
test('invariant document type with invariant tiptap RTE with invariant block with an invariant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
@@ -64,57 +65,41 @@ test('invariant document type with invariant tiptap RTE with invariant block wit
|
||||
await umbracoUi.content.doesPropertyContainValue(textStringName, textStringText);
|
||||
});
|
||||
|
||||
test('invariant document type with invariant tiptap RTE with variant block with an invariant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
test('can not create unsupported invariant document type with invariant tiptap RTE with variant block with an invariant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementTypeWithVaryByCulture(blockName, elementGroupName, textStringName, textStringDataTypeId, true, false);
|
||||
tipTapId = await umbracoApi.dataType.createTipTapDataTypeWithABlock(tipTapName, elementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, tipTapName, tipTapId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickInsertBlockButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
await umbracoUi.content.enterTextstring(textStringText);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.unsupportInvariantContentItemWithVariantBlocks);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
|
||||
await umbracoUi.reloadPage();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
await umbracoUi.content.doesPropertyContainValue(textStringName, textStringText);
|
||||
await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished);
|
||||
});
|
||||
|
||||
// Remove fixme when this test works. Currently the textstring value is is not saved when saving / publishing the document
|
||||
test.fixme('invariant document type with invariant tiptap RTE with variant block with an variant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
test('can not create unsupported invariant document type with invariant tiptap RTE with variant block with an variant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
elementTypeId = await umbracoApi.documentType.createDefaultElementTypeWithVaryByCulture(blockName, elementGroupName, textStringName, textStringDataTypeId, true, true);
|
||||
tipTapId = await umbracoApi.dataType.createTipTapDataTypeWithABlock(tipTapName, elementTypeId);
|
||||
documentTypeId = await umbracoApi.documentType.createDocumentTypeWithPropertyEditor(documentTypeName, tipTapName, tipTapId, documentTypeGroupName);
|
||||
await umbracoApi.document.createDefaultDocument(contentName, documentTypeId);
|
||||
await umbracoUi.goToBackOffice();
|
||||
await umbracoUi.content.goToSection(ConstantHelper.sections.content);
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.content.clickInsertBlockButton();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
await umbracoUi.content.enterTextstring(textStringText);
|
||||
await umbracoUi.content.clickCreateModalButton();
|
||||
await umbracoUi.content.goToContentWithName(contentName);
|
||||
await umbracoUi.content.isValidationMessageVisible(ConstantHelper.validationMessages.unsupportInvariantContentItemWithVariantBlocks);
|
||||
await umbracoUi.content.clickSaveAndPublishButton();
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.saved);
|
||||
await umbracoUi.content.doesSuccessNotificationHaveText(NotificationConstantHelper.success.published);
|
||||
|
||||
await umbracoUi.reloadPage();
|
||||
await umbracoUi.content.clickBlockElementWithName(blockName);
|
||||
await umbracoUi.content.doesPropertyContainValue(textStringName, textStringText);
|
||||
await umbracoUi.content.doesErrorNotificationHaveText(NotificationConstantHelper.error.documentCouldNotBePublished);
|
||||
});
|
||||
|
||||
test('variant document type with variant tiptap RTE with variant block with an variant textString', async ({umbracoApi, umbracoUi}) => {
|
||||
|
||||
+55
@@ -544,6 +544,35 @@ internal sealed partial class ContentTypeEditingServiceTests
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Create_Child_To_Content_Type_With_Composition()
|
||||
{
|
||||
var compositionContentType = (await ContentTypeEditingService.CreateAsync(ContentTypeCreateModel("Composition"), Constants.Security.SuperUserKey)).Result!;
|
||||
var parentContentType = (await ContentTypeEditingService.CreateAsync(
|
||||
ContentTypeCreateModel(
|
||||
"Parent",
|
||||
compositions: [new Composition { CompositionType = CompositionType.Composition, Key = compositionContentType.Key }]),
|
||||
Constants.Security.SuperUserKey)).Result!;
|
||||
var result = await ContentTypeEditingService.CreateAsync(
|
||||
ContentTypeCreateModel(
|
||||
"Child",
|
||||
compositions: [new Composition { CompositionType = CompositionType.Inheritance, Key = parentContentType.Key }]),
|
||||
Constants.Security.SuperUserKey);
|
||||
|
||||
Assert.IsTrue(result.Success);
|
||||
|
||||
// Ensure it's actually persisted
|
||||
var childContentType = await ContentTypeService.GetAsync(result.Result!.Key);
|
||||
|
||||
Assert.IsNotNull(childContentType);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("Child", childContentType.Name);
|
||||
Assert.AreEqual(1, childContentType.ContentTypeComposition.Count());
|
||||
Assert.AreEqual(parentContentType.Key, childContentType.ContentTypeComposition.Single().Key);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Be_Both_Parent_And_Composition()
|
||||
{
|
||||
@@ -691,6 +720,32 @@ internal sealed partial class ContentTypeEditingServiceTests
|
||||
Assert.AreEqual(ContentTypeOperationStatus.InvalidParent, result.Status);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Have_Same_Key_For_Inheritance_And_Parent()
|
||||
{
|
||||
var parentModel = ContentTypeCreateModel("Parent");
|
||||
var parent = (await ContentTypeEditingService.CreateAsync(parentModel, Constants.Security.SuperUserKey)).Result;
|
||||
Assert.IsNotNull(parent);
|
||||
|
||||
Composition[] composition =
|
||||
{
|
||||
new()
|
||||
{
|
||||
CompositionType = CompositionType.Inheritance, Key = parent.Key,
|
||||
}
|
||||
};
|
||||
|
||||
var childModel = ContentTypeCreateModel(
|
||||
"Child",
|
||||
containerKey: parent.Key,
|
||||
compositions: composition);
|
||||
|
||||
var result = await ContentTypeEditingService.CreateAsync(childModel, Constants.Security.SuperUserKey);
|
||||
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(ContentTypeOperationStatus.InvalidParent, result.Status);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Use_As_ParentKey()
|
||||
{
|
||||
|
||||
+124
@@ -601,6 +601,91 @@ internal sealed partial class ContentTypeEditingServiceTests
|
||||
Assert.AreEqual(567, contentType.HistoryCleanup.KeepLatestVersionPerDayForDays);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Reapply_Compositions_For_Content_Type_With_Children()
|
||||
{
|
||||
var compositionContentType = (await ContentTypeEditingService.CreateAsync(ContentTypeCreateModel("Composition"), Constants.Security.SuperUserKey)).Result!;
|
||||
var parentContentType = (await ContentTypeEditingService.CreateAsync(
|
||||
ContentTypeCreateModel(
|
||||
"Parent",
|
||||
compositions: [new Composition { CompositionType = CompositionType.Composition, Key = compositionContentType.Key }]),
|
||||
Constants.Security.SuperUserKey)).Result!;
|
||||
var childContentType = (await ContentTypeEditingService.CreateAsync(
|
||||
ContentTypeCreateModel(
|
||||
"Child",
|
||||
compositions: [new Composition { CompositionType = CompositionType.Inheritance, Key = parentContentType.Key }]),
|
||||
Constants.Security.SuperUserKey)).Result!;
|
||||
|
||||
var updateModel = ContentTypeUpdateModel(
|
||||
"Parent Updated",
|
||||
compositions: [new() { CompositionType = CompositionType.Composition, Key = compositionContentType.Key }]);
|
||||
|
||||
var result = await ContentTypeEditingService.UpdateAsync(parentContentType, updateModel, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(result.Success);
|
||||
|
||||
// Ensure it's actually persisted
|
||||
parentContentType = await ContentTypeService.GetAsync(parentContentType.Key);
|
||||
|
||||
Assert.IsNotNull(parentContentType);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("Parent Updated", parentContentType.Name);
|
||||
Assert.AreEqual(1, parentContentType.ContentTypeComposition.Count());
|
||||
Assert.AreEqual(compositionContentType.Key, parentContentType.ContentTypeComposition.Single().Key);
|
||||
});
|
||||
|
||||
childContentType = await ContentTypeService.GetAsync(childContentType.Key);
|
||||
|
||||
Assert.IsNotNull(childContentType);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("Child", childContentType.Name);
|
||||
Assert.AreEqual(1, childContentType.ContentTypeComposition.Count());
|
||||
Assert.AreEqual(parentContentType.Key, childContentType.ContentTypeComposition.Single().Key);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Remove_Compositions_For_Content_Type_With_Children()
|
||||
{
|
||||
var compositionContentType = (await ContentTypeEditingService.CreateAsync(ContentTypeCreateModel("Composition"), Constants.Security.SuperUserKey)).Result!;
|
||||
var parentContentType = (await ContentTypeEditingService.CreateAsync(
|
||||
ContentTypeCreateModel(
|
||||
"Parent",
|
||||
compositions: [new Composition { CompositionType = CompositionType.Composition, Key = compositionContentType.Key }]),
|
||||
Constants.Security.SuperUserKey)).Result!;
|
||||
var childContentType = (await ContentTypeEditingService.CreateAsync(
|
||||
ContentTypeCreateModel(
|
||||
"Child",
|
||||
compositions: [new Composition { CompositionType = CompositionType.Inheritance, Key = parentContentType.Key }]),
|
||||
Constants.Security.SuperUserKey)).Result!;
|
||||
|
||||
var updateModel = ContentTypeUpdateModel("Parent Updated", compositions: []);
|
||||
|
||||
var result = await ContentTypeEditingService.UpdateAsync(parentContentType, updateModel, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(result.Success);
|
||||
|
||||
// Ensure it's actually persisted
|
||||
parentContentType = await ContentTypeService.GetAsync(parentContentType.Key);
|
||||
|
||||
Assert.IsNotNull(parentContentType);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("Parent Updated", parentContentType.Name);
|
||||
Assert.IsEmpty(parentContentType.ContentTypeComposition);
|
||||
});
|
||||
|
||||
childContentType = await ContentTypeService.GetAsync(childContentType.Key);
|
||||
|
||||
Assert.IsNotNull(childContentType);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("Child", childContentType.Name);
|
||||
Assert.AreEqual(1, childContentType.ContentTypeComposition.Count());
|
||||
Assert.AreEqual(parentContentType.Key, childContentType.ContentTypeComposition.Single().Key);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public async Task Cannot_Move_Properties_To_Non_Existing_Containers(bool isElement)
|
||||
@@ -826,4 +911,43 @@ internal sealed partial class ContentTypeEditingServiceTests
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(ContentTypeOperationStatus.InvalidContainerType, result.Status);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Add_Compositions_For_Content_Type_With_Children()
|
||||
{
|
||||
var compositionContentType = (await ContentTypeEditingService.CreateAsync(ContentTypeCreateModel("Composition"), Constants.Security.SuperUserKey)).Result!;
|
||||
var parentContentType = (await ContentTypeEditingService.CreateAsync(ContentTypeCreateModel("Parent"), Constants.Security.SuperUserKey)).Result!;
|
||||
var childContentType = (await ContentTypeEditingService.CreateAsync(
|
||||
ContentTypeCreateModel(
|
||||
"Child",
|
||||
compositions: [new Composition { CompositionType = CompositionType.Inheritance, Key = parentContentType.Key }]),
|
||||
Constants.Security.SuperUserKey)).Result!;
|
||||
|
||||
var updateModel = ContentTypeUpdateModel(
|
||||
"Parent Updated",
|
||||
compositions: [new() { CompositionType = CompositionType.Composition, Key = compositionContentType.Key }]);
|
||||
|
||||
var result = await ContentTypeEditingService.UpdateAsync(parentContentType, updateModel, Constants.Security.SuperUserKey);
|
||||
Assert.IsFalse(result.Success);
|
||||
|
||||
// Ensure nothing was persisted
|
||||
parentContentType = await ContentTypeService.GetAsync(parentContentType.Key);
|
||||
|
||||
Assert.IsNotNull(parentContentType);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("Parent", parentContentType.Name);
|
||||
Assert.AreEqual(0, parentContentType.ContentTypeComposition.Count());
|
||||
});
|
||||
|
||||
childContentType = await ContentTypeService.GetAsync(childContentType.Key);
|
||||
|
||||
Assert.IsNotNull(childContentType);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual("Child", childContentType.Name);
|
||||
Assert.AreEqual(1, childContentType.ContentTypeComposition.Count());
|
||||
Assert.AreEqual(parentContentType.Key, childContentType.ContentTypeComposition.Single().Key);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Api.Common.Serialization;
|
||||
using Umbraco.Cms.Api.Management.Serialization;
|
||||
using Umbraco.Cms.Tests.UnitTests.TestHelpers;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Cms.Api.Management.Serialization;
|
||||
|
||||
[TestFixture]
|
||||
public class BackOfficeSerializationTests
|
||||
{
|
||||
private JsonOptions jsonOptions;
|
||||
|
||||
[SetUp]
|
||||
public void SetupOptions()
|
||||
{
|
||||
var typeInfoResolver = new UmbracoJsonTypeInfoResolver(TestHelper.GetTypeFinder());
|
||||
var configurationOptions = new ConfigureUmbracoBackofficeJsonOptions(typeInfoResolver);
|
||||
var options = new JsonOptions();
|
||||
configurationOptions.Configure(global::Umbraco.Cms.Core.Constants.JsonOptionsNames.BackOffice, options);
|
||||
jsonOptions = options;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Will_Serialize_To_Camel_Case()
|
||||
{
|
||||
var objectToSerialize = new UnNestedJsonTestValue();
|
||||
|
||||
var json = JsonSerializer.Serialize(objectToSerialize, jsonOptions.JsonSerializerOptions);
|
||||
|
||||
Assert.AreEqual("{\"stringValue\":\"theValue\"}", json);
|
||||
}
|
||||
|
||||
// the limit is 64, but it seems like the functional limit is that minus 1
|
||||
[TestCase(1, true, TestName = "Can_Serialize_At_Min_Depth(1)")]
|
||||
[TestCase(48, true, TestName = "Can_Serialize_At_High_Depth(33)")]
|
||||
[TestCase(63, true, TestName = "Can_Serialize_To_Max_Depth(63)")]
|
||||
[TestCase(64, false, TestName = "Can_NOT_Serialize_Beyond_Max_Depth(64)")]
|
||||
public void Can_Serialize_To_Max_Depth(int depth, bool shouldPass)
|
||||
{
|
||||
var objectToSerialize = CreateNestedObject(depth);
|
||||
|
||||
if (shouldPass)
|
||||
{
|
||||
var json = JsonSerializer.Serialize(objectToSerialize, jsonOptions.JsonSerializerOptions);
|
||||
Assert.IsNotEmpty(json);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Throws<JsonException>(() => JsonSerializer.Serialize(objectToSerialize, jsonOptions.JsonSerializerOptions));
|
||||
}
|
||||
}
|
||||
|
||||
private static NestedJsonTestValue CreateNestedObject(int levels)
|
||||
{
|
||||
var root = new NestedJsonTestValue { Level = 1 };
|
||||
var outer = root;
|
||||
for (var i = 2; i <= levels; i++)
|
||||
{
|
||||
var inner = new NestedJsonTestValue { Level = i };
|
||||
outer.Inner = inner;
|
||||
outer = inner;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
public class UnNestedJsonTestValue
|
||||
{
|
||||
public string StringValue { get; set; } = "theValue";
|
||||
}
|
||||
|
||||
public class NestedJsonTestValue
|
||||
{
|
||||
public int Level { get; set; }
|
||||
|
||||
public NestedJsonTestValue? Inner { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user