Files
Umbraco-CMS/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ElementSwitchValidatorTests.cs
Laura Neto 55506bac3a Simplify creating content from a blueprint programmatically (#19528)
* 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>
2025-06-24 13:43:34 +02:00

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);
}
}