* Rename `IContentService.CreateContentFromBlueprint` to `CreateBlueprintFromContent` In reality, this method is used by the core to create a blueprint from content, and not the other way around, which doesn't need new ids. This was causing confusion, so the old name has been marked as deprecated in favor of the new name. If developers want to create content from blueprints they should use `IContentBlueprintEditingService.GetScaffoldedAsync()` instead, which is what is used by the management api. * Added integration tests to verify that new block ids are generated when creating content from a blueprint * Return copy of the blueprint in `ContentBlueprintEditingService.GetScaffoldedAsync` instead of the blueprint itself * Update CreateContentFromBlueprint xml docs to mention both replacement methods * Fix tests for rich text blocks * Small re-organization * Adjusted tests that were still referencing `ContentService.CreateContentFromBlueprint` * Add default implementation to new CreateBlueprintFromContent method * Update tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentBlueprintEditingServiceTests.GetScaffold.cs Co-authored-by: Andy Butland <abutland73@gmail.com> --------- Co-authored-by: Andy Butland <abutland73@gmail.com>
298 lines
12 KiB
C#
298 lines
12 KiB
C#
using NUnit.Framework;
|
|
using Umbraco.Cms.Core;
|
|
using Umbraco.Cms.Core.Extensions;
|
|
using Umbraco.Cms.Core.Models;
|
|
using Umbraco.Cms.Core.PropertyEditors;
|
|
using Umbraco.Cms.Core.Services;
|
|
using Umbraco.Cms.Core.Services.ContentTypeEditing;
|
|
using Umbraco.Cms.Tests.Common.Attributes;
|
|
using Umbraco.Cms.Tests.Common.Builders;
|
|
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
|
using Umbraco.Cms.Tests.Common.Testing;
|
|
using Umbraco.Cms.Tests.Integration.Testing;
|
|
|
|
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
|
|
|
|
[TestFixture]
|
|
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
|
|
internal sealed class ElementSwitchValidatorTests : UmbracoIntegrationTest
|
|
{
|
|
private IElementSwitchValidator ElementSwitchValidator => GetRequiredService<IElementSwitchValidator>();
|
|
|
|
private IContentTypeService ContentTypeService => GetRequiredService<IContentTypeService>();
|
|
|
|
private IContentService ContentService => GetRequiredService<IContentService>();
|
|
|
|
private IDataTypeService DataTypeService => GetRequiredService<IDataTypeService>();
|
|
|
|
[TestCase(new[] { true }, 0, true, true, TestName = "E=>E No Ancestor or children")]
|
|
[TestCase(new[] { false }, 0, false, true, TestName = "D=>D No Ancestor or children")]
|
|
[TestCase(new[] { true }, 0, false, true, TestName = "E=>D No Ancestor or children")]
|
|
[TestCase(new[] { false }, 0, true, true, TestName = "D=>E No Ancestor or children")]
|
|
[TestCase(new[] { true, true }, 1, true, true, TestName = "E Valid Parent")]
|
|
[TestCase(new[] { true, true }, 0, true, true, TestName = "E Valid Child")]
|
|
[TestCase(new[] { false, false }, 1, false, true, TestName = "D Valid Parent")]
|
|
[TestCase(new[] { false, false }, 0, false, true, TestName = "D Valid Child")]
|
|
[TestCase(new[] { false, false }, 1, true, false, TestName = "E InValid Parent")]
|
|
[TestCase(new[] { false, false }, 0, true, true, TestName = "E InValid Child")]
|
|
[TestCase(new[] { true, true }, 1, false, false, TestName = "D InValid Parent")]
|
|
[TestCase(new[] { true, true }, 0, false, true, TestName = "D InValid Child")]
|
|
[TestCase(
|
|
new[] { true, false, false, true, false },
|
|
2,
|
|
true,
|
|
false,
|
|
TestName = "D=>E InValid Child, Invalid Parent")]
|
|
[TestCase(
|
|
new[] { false, true, false, true, false },
|
|
2,
|
|
true,
|
|
false,
|
|
TestName = "D=>E InValid Child, Invalid Ancestor")]
|
|
[TestCase(
|
|
new[] { true, false, false, true, true },
|
|
2,
|
|
true,
|
|
false,
|
|
TestName = "D=>E Valid Children, Invalid Parent")]
|
|
[TestCase(
|
|
new[] { false, true, false, true, true },
|
|
2,
|
|
true,
|
|
false,
|
|
TestName = "D=>E Valid Children, Invalid Ancestor")]
|
|
[TestCase(new[] { false, false, false, false, false }, 2, true, false, TestName = "D=>E mismatch")]
|
|
[TestCase(new[] { false, false, true, false, false }, 2, false, true, TestName = "D=>E correction")]
|
|
[TestCase(new[] { true, true, true, true, true }, 2, false, false, TestName = "E=>D mismatch")]
|
|
[TestCase(new[] { true, true, false, true, true }, 2, true, true, TestName = "E=>D correction")]
|
|
[LongRunning]
|
|
public async Task AncestorsAreAligned(
|
|
bool[] isElementDoctypeChain,
|
|
int itemToTestIndex,
|
|
bool itemToTestNewIsElementValue,
|
|
bool validationShouldPass)
|
|
{
|
|
// Arrange
|
|
IContentType? parentItem = null;
|
|
IContentType? itemToTest = null;
|
|
for (var index = 0; index < isElementDoctypeChain.Length; index++)
|
|
{
|
|
var itemIsElement = isElementDoctypeChain[index];
|
|
var builder = new ContentTypeBuilder()
|
|
.WithIsElement(itemIsElement);
|
|
if (parentItem is not null)
|
|
{
|
|
builder.WithParentContentType(parentItem);
|
|
}
|
|
|
|
var contentType = builder.Build();
|
|
await ContentTypeService.CreateAsync(contentType, Constants.Security.SuperUserKey);
|
|
parentItem = contentType;
|
|
if (index == itemToTestIndex)
|
|
{
|
|
itemToTest = contentType;
|
|
}
|
|
}
|
|
|
|
// Act
|
|
itemToTest!.IsElement = itemToTestNewIsElementValue;
|
|
var result = await ElementSwitchValidator.AncestorsAreAlignedAsync(itemToTest);
|
|
|
|
// Assert
|
|
Assert.AreEqual(result, validationShouldPass);
|
|
}
|
|
|
|
[TestCase(new[] { true }, 0, true, true, TestName = "E=>E No Ancestor or children")]
|
|
[TestCase(new[] { false }, 0, false, true, TestName = "D=>D No Ancestor or children")]
|
|
[TestCase(new[] { true }, 0, false, true, TestName = "E=>D No Ancestor or children")]
|
|
[TestCase(new[] { false }, 0, true, true, TestName = "D=>E No Ancestor or children")]
|
|
[TestCase(new[] { true, true }, 1, true, true, TestName = "E Valid Parent")]
|
|
[TestCase(new[] { true, true }, 0, true, true, TestName = "E Valid Child")]
|
|
[TestCase(new[] { false, false }, 1, false, true, TestName = "D Valid Parent")]
|
|
[TestCase(new[] { false, false }, 0, false, true, TestName = "D Valid Child")]
|
|
[TestCase(new[] { false, false }, 1, true, true, TestName = "E InValid Parent")]
|
|
[TestCase(new[] { false, false }, 0, true, false, TestName = "E InValid Child")]
|
|
[TestCase(new[] { true, true }, 1, false, true, TestName = "D InValid Parent")]
|
|
[TestCase(new[] { true, true }, 0, false, false, TestName = "D InValid Child")]
|
|
[TestCase(
|
|
new[] { true, false, false, true, false },
|
|
2,
|
|
true,
|
|
false,
|
|
TestName = "D=>E InValid Child, Invalid Parent")]
|
|
[TestCase(
|
|
new[] { false, true, false, true, false },
|
|
2,
|
|
true,
|
|
false,
|
|
TestName = "D=>E InValid Child, Invalid Ancestor")]
|
|
[TestCase(
|
|
new[] { true, false, false, true, true },
|
|
2,
|
|
true,
|
|
true,
|
|
TestName = "D=>E Valid Children, Invalid Parent")]
|
|
[TestCase(new[] { false, true, false, true, true },
|
|
2,
|
|
true,
|
|
true,
|
|
TestName = "D=>E Valid Children, Invalid Ancestor")]
|
|
[TestCase(new[] { false, false, false, false, false }, 2, true, false, TestName = "D=>E mismatch")]
|
|
[TestCase(new[] { false, false, true, false, false }, 2, false, true, TestName = "D=>E correction")]
|
|
[TestCase(new[] { true, true, true, true, true }, 2, false, false, TestName = "E=>D mismatch")]
|
|
[TestCase(new[] { true, true, false, true, true }, 2, true, true, TestName = "E=>D correction")]
|
|
[LongRunning]
|
|
public async Task DescendantsAreAligned(
|
|
bool[] isElementDoctypeChain,
|
|
int itemToTestIndex,
|
|
bool itemToTestNewIsElementValue,
|
|
bool validationShouldPass)
|
|
{
|
|
// Arrange
|
|
IContentType? parentItem = null;
|
|
IContentType? itemToTest = null;
|
|
for (var index = 0; index < isElementDoctypeChain.Length; index++)
|
|
{
|
|
var itemIsElement = isElementDoctypeChain[index];
|
|
var builder = new ContentTypeBuilder()
|
|
.WithIsElement(itemIsElement);
|
|
if (parentItem is not null)
|
|
{
|
|
builder.WithParentContentType(parentItem);
|
|
}
|
|
|
|
var contentType = builder.Build();
|
|
await ContentTypeService.CreateAsync(contentType, Constants.Security.SuperUserKey);
|
|
parentItem = contentType;
|
|
if (index == itemToTestIndex)
|
|
{
|
|
itemToTest = contentType;
|
|
}
|
|
}
|
|
|
|
// Act
|
|
itemToTest!.IsElement = itemToTestNewIsElementValue;
|
|
var result = await ElementSwitchValidator.DescendantsAreAlignedAsync(itemToTest);
|
|
|
|
// Assert
|
|
Assert.AreEqual(result, validationShouldPass);
|
|
}
|
|
|
|
[TestCase(0, true, TestName = "No Content")]
|
|
[TestCase(1, false, TestName = "One Content Item")]
|
|
[TestCase(5, false, TestName = "Many Content Items")]
|
|
public async Task DocumentToElementHasNoContent(int amountOfDocumentsCreated, bool validationShouldPass)
|
|
{
|
|
// Arrange
|
|
var contentType = await SetupContentType(false);
|
|
|
|
for (int i = 0; i < amountOfDocumentsCreated; i++)
|
|
{
|
|
var contentBuilder = new ContentBuilder().WithContentType(contentType);
|
|
var content = contentBuilder.Build();
|
|
ContentService.Save(content);
|
|
}
|
|
|
|
// Act
|
|
contentType.IsElement = true;
|
|
var result = await ElementSwitchValidator.DocumentToElementHasNoContentAsync(contentType);
|
|
|
|
// Assert
|
|
Assert.AreEqual(result, validationShouldPass);
|
|
}
|
|
|
|
// Since the full permutation table would result in 64 tests and more block editors might be added later,
|
|
// we will at least test each single failure and a few combinations
|
|
// used in none
|
|
[TestCase(false, false, false, false, false, false, true)]
|
|
// used in one
|
|
[TestCase(true, false, false, false, false, false, false)]
|
|
[TestCase(false, true, false, false, false, false, false)]
|
|
[TestCase(false, false, true, false, false, false, false)]
|
|
[TestCase(false, false, false, true, false, false, false)]
|
|
[TestCase(false, false, false, false, true, false, false)]
|
|
[TestCase(false, false, false, false, false, true, false)]
|
|
// used in selection and setting
|
|
[TestCase(true, true, false, false, false, false, false)]
|
|
// used in 2 selections
|
|
[TestCase(true, false, true, false, false, false, false)]
|
|
// used in 2 settings
|
|
[TestCase(false, true, false, false, false, true, false)]
|
|
// used in all
|
|
[TestCase(true, true, true, true, true, true, false)]
|
|
public async Task ElementToDocumentNotUsedInBlockStructures(
|
|
bool isUsedInBlockList,
|
|
bool isUsedInBlockListBlockSetting,
|
|
bool isUsedInBlockGrid,
|
|
bool isUsedInBlockGridBlockSetting,
|
|
bool isUsedInRte,
|
|
bool isUsedInRteBlockSetting,
|
|
bool validationShouldPass)
|
|
{
|
|
// Arrange
|
|
var elementType = await SetupContentType(true);
|
|
|
|
var otherElementType = await SetupContentType(true);
|
|
|
|
if (isUsedInBlockList)
|
|
{
|
|
await SetupDataType(Constants.PropertyEditors.Aliases.BlockList, elementType.Key, null);
|
|
}
|
|
|
|
if (isUsedInBlockListBlockSetting)
|
|
{
|
|
await SetupDataType(Constants.PropertyEditors.Aliases.BlockList, otherElementType.Key, elementType.Key);
|
|
}
|
|
|
|
if (isUsedInBlockGrid)
|
|
{
|
|
await SetupDataType(Constants.PropertyEditors.Aliases.BlockGrid, elementType.Key, null);
|
|
}
|
|
|
|
if (isUsedInBlockGridBlockSetting)
|
|
{
|
|
await SetupDataType(Constants.PropertyEditors.Aliases.BlockGrid, otherElementType.Key, elementType.Key);
|
|
}
|
|
|
|
if (isUsedInRte)
|
|
{
|
|
await SetupDataType(Constants.PropertyEditors.Aliases.RichText, elementType.Key, null);
|
|
}
|
|
|
|
if (isUsedInRteBlockSetting)
|
|
{
|
|
await SetupDataType(Constants.PropertyEditors.Aliases.RichText, otherElementType.Key, elementType.Key);
|
|
}
|
|
|
|
// Act
|
|
var result = await ElementSwitchValidator.ElementToDocumentNotUsedInBlockStructuresAsync(elementType);
|
|
|
|
// Assert
|
|
Assert.AreEqual(result, validationShouldPass);
|
|
}
|
|
|
|
private async Task<IContentType> SetupContentType(bool isElement)
|
|
{
|
|
var typeBuilder = new ContentTypeBuilder()
|
|
.WithIsElement(isElement);
|
|
var contentType = typeBuilder.Build();
|
|
await ContentTypeService.CreateAsync(contentType, Constants.Security.SuperUserKey);
|
|
return contentType;
|
|
}
|
|
|
|
// Constants.PropertyEditors.Aliases.BlockGrid
|
|
private async Task SetupDataType(
|
|
string editorAlias,
|
|
Guid elementKey,
|
|
Guid? elementSettingKey)
|
|
{
|
|
var dataType = DataTypeBuilder.CreateSimpleElementDataType(
|
|
IOHelper,
|
|
editorAlias,
|
|
elementKey,
|
|
elementSettingKey);
|
|
|
|
await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey);
|
|
}
|
|
}
|