* Fix warnings SA1111, SA1028, SA1500, IDE1270 in Umbraco.Web.Website, and updated rules. * Remove warnings: IDE0270: Null check can be simplified * More SqlServer project warnings resolved * CS0105 namespace appeared already * Suppress warning until implementation: #pragma warning disable CS0162 // Unreachable code detected #pragma warning disable CS0618 // Type or member is obsolete CS0162 remove unreachable code SA1028 remove trailing whitespace SA1106 no empty statements CS1570 malformed XML CS1572 corrected xml parameter CS1573 param tag added IDE0007 var not explicit IDE0008 explicit not var IDE0057 simplify substring IDE0074 compound assignment CA1825 array.empty Down to 3479 warnings * - SA1116, SA117 params on same line - IDE0057 substring simplified Specific warnings for Umbraco.Tests.Benchmarks * Fixed IDE0074 compound assignment and added specific warnings for Umbraco.Tests.Common * Specific warnings for Umbraco.Tests.Integration and Umbraco.Tests.Common Fixed: - SA1111, SA1116, SA117 params and line formatting (not all as there are many) - SA1122 string.Empty - IDE0057 simplify substring - IDE0044,IDE0044 make field readonly - IDE1006 naming rule violation (add _) - SA1111 closing parenthesis on line of last parameter - SA1649 filename match type name - SA1312,SA1306 lowercase variable and field names * Fixed various warnings where they are more straight-forward, including: - SA1649 file name match type name - SA111 parenthesis on line of last parameter - IDE0028 simplify collection initializer - SA1306 lower-case letter field - IDE044 readonly field - SA1122 string.Empty - SA1116 params same line - IDE1006 upper casing - IDE0041 simplify null check Updated the following projects to only list their remaining specific warning codes: - Umbraco.Tests.UnitTests Typo in `Umbraco.Web.Website` project * Reverted test change * Now 1556 warnings. Fixed various warnings where they are more straight-forward, including: - SA1111/SA1116/SA1119 parenthesis - SA1117 params - SA1312 lowercase variable - SA1121 built-in type - SA1500/SA1513/SA1503 formatting braces - SA1400 declare access modifier - SA1122 string.Empty - SA1310 no underscore - IDE0049 name simplified - IDE0057 simplify substring - IDE0074 compound assignment - IDE0032 use auto-property - IDE0037 simplify member name - IDE0008 explicit type not var - IDE0016/IDE0270/IDE0041 simplify null checks - IDE0048/SA1407 clarity in arithmetic - IDE1006 correct param names - IDE0042 deconstruct variable - IDE0044 readonly - IDE0018 inline variable declarations - IDE0074/IDE0054 compound assignment - IDE1006 naming - CS1573 param XML - CS0168 unused variable Comment formatting in project files for consistency. Updated all projects to only list remaining specific warning codes as warnings instead of errors (errors is now default). * Type not var, and more warning exceptions * Tweaked merge issue, readded comment about rollback * Readded comment re rollback. * Readded comments * Comment tweak * Comment tweak
390 lines
15 KiB
C#
390 lines
15 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)]
|
|
public 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)
|
|
{
|
|
Dictionary<string, object> configuration;
|
|
switch (editorAlias)
|
|
{
|
|
case Constants.PropertyEditors.Aliases.BlockGrid:
|
|
configuration = GetBlockGridBaseConfiguration();
|
|
break;
|
|
case Constants.PropertyEditors.Aliases.RichText:
|
|
configuration = GetRteBaseConfiguration();
|
|
break;
|
|
default:
|
|
configuration = new Dictionary<string, object>();
|
|
break;
|
|
}
|
|
|
|
SetBlockConfiguration(
|
|
configuration,
|
|
elementKey,
|
|
elementSettingKey,
|
|
editorAlias == Constants.PropertyEditors.Aliases.BlockGrid ? true : null);
|
|
|
|
|
|
var dataTypeBuilder = new DataTypeBuilder()
|
|
.WithId(0)
|
|
.WithDatabaseType(ValueStorageType.Nvarchar)
|
|
.AddEditor()
|
|
.WithAlias(editorAlias);
|
|
|
|
switch (editorAlias)
|
|
{
|
|
case Constants.PropertyEditors.Aliases.BlockGrid:
|
|
dataTypeBuilder.WithConfigurationEditor(
|
|
new BlockGridConfigurationEditor(IOHelper) { DefaultConfiguration = configuration });
|
|
break;
|
|
case Constants.PropertyEditors.Aliases.BlockList:
|
|
dataTypeBuilder.WithConfigurationEditor(
|
|
new BlockListConfigurationEditor(IOHelper) { DefaultConfiguration = configuration });
|
|
break;
|
|
case Constants.PropertyEditors.Aliases.RichText:
|
|
dataTypeBuilder.WithConfigurationEditor(
|
|
new RichTextConfigurationEditor(IOHelper) { DefaultConfiguration = configuration });
|
|
break;
|
|
}
|
|
|
|
var dataType = dataTypeBuilder.Done()
|
|
.Build();
|
|
|
|
await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey);
|
|
}
|
|
|
|
private void SetBlockConfiguration(
|
|
Dictionary<string, object> dictionary,
|
|
Guid? elementKey,
|
|
Guid? elementSettingKey,
|
|
bool? allowAtRoot)
|
|
{
|
|
if (elementKey is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
dictionary["blocks"] = new[] { BuildBlockConfiguration(elementKey.Value, elementSettingKey, allowAtRoot) };
|
|
}
|
|
|
|
private Dictionary<string, object> GetBlockGridBaseConfiguration()
|
|
=> new Dictionary<string, object> { ["gridColumns"] = 12 };
|
|
|
|
private Dictionary<string, object> GetRteBaseConfiguration()
|
|
{
|
|
var dictionary = new Dictionary<string, object>
|
|
{
|
|
["maxImageSize"] = 500,
|
|
["mode"] = "Classic",
|
|
["toolbar"] = new[]
|
|
{
|
|
"styles", "bold", "italic", "alignleft", "aligncenter", "alignright", "bullist", "numlist",
|
|
"outdent", "indent", "sourcecode", "link", "umbmediapicker", "umbembeddialog"
|
|
},
|
|
};
|
|
return dictionary;
|
|
}
|
|
|
|
private Dictionary<string, object> BuildBlockConfiguration(
|
|
Guid? elementKey,
|
|
Guid? elementSettingKey,
|
|
bool? allowAtRoot)
|
|
{
|
|
var dictionary = new Dictionary<string, object>();
|
|
if (allowAtRoot is not null)
|
|
{
|
|
dictionary.Add("allowAtRoot", allowAtRoot.Value);
|
|
}
|
|
|
|
dictionary.Add("contentElementTypeKey", elementKey.ToString());
|
|
if (elementSettingKey is not null)
|
|
{
|
|
dictionary.Add("settingsElementTypeKey", elementSettingKey.ToString());
|
|
}
|
|
|
|
return dictionary;
|
|
}
|
|
}
|