Files
Umbraco-CMS/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs
Paul Johnson 00133e880d Move test projects from src/ to tests/ (#11357)
* Update gitignore

* Move csproj

* Update project references

* Update solutions

* Update build scripts

* Tests used to share editorconfig with projects in src

* Fix broken tests.

* Stop copying around .editorconfig

merged root one with linting

* csharp_style_expression_bodied -> suggestion

* Move StyleCop rulesets to matching directories and update shared build properties

* Remove legacy build files, update NuGet.cofig and solution files

* Restore myget source

* Clean up .gitignore

* Update .gitignore

* Move new test classes to tests after merge

* Gitignore + nuget config

* Move new test

Co-authored-by: Ronald Barendse <ronald@barend.se>
2021-10-18 08:14:04 +01:00

489 lines
19 KiB
C#

// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Dashboards;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Manifest;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.PropertyEditors.Validators;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
using Umbraco.Cms.Infrastructure.Serialization;
using Umbraco.Cms.Tests.UnitTests.TestHelpers;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Manifest
{
[TestFixture]
public class ManifestParserTests
{
private ManifestParser _parser;
private IIOHelper _ioHelper;
[SetUp]
public void Setup()
{
var validators = new IManifestValueValidator[]
{
new RequiredValidator(Mock.Of<ILocalizedTextService>()),
new RegexValidator(Mock.Of<ILocalizedTextService>(), null),
new DelimitedValueValidator(),
};
_ioHelper = TestHelper.IOHelper;
NullLoggerFactory loggerFactory = NullLoggerFactory.Instance;
_parser = new ManifestParser(
AppCaches.Disabled,
new ManifestValueValidatorCollection(() => validators),
new ManifestFilterCollection(() => Enumerable.Empty<IManifestFilter>()),
loggerFactory.CreateLogger<ManifestParser>(),
_ioHelper,
TestHelper.GetHostingEnvironment(),
new JsonNetSerializer(),
Mock.Of<ILocalizedTextService>(),
Mock.Of<IShortStringHelper>(),
Mock.Of<IDataValueEditorFactory>());
}
[Test]
public void DelimitedValueValidator()
{
const string json = @"{'propertyEditors': [
{
alias: 'Test.Test2',
name: 'Test 2',
isParameterEditor: true,
defaultConfig: { key1: 'some default val' },
editor: {
view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html',
valueType: 'int',
validation: {
delimited: {
delimiter: ',',
pattern: '^[a-zA-Z]*$'
}
}
}
}
]}";
PackageManifest manifest = _parser.ParseManifest(json);
Assert.AreEqual(1, manifest.ParameterEditors.Length);
Assert.AreEqual(1, manifest.ParameterEditors[0].GetValueEditor().Validators.Count);
Assert.IsTrue(manifest.ParameterEditors[0].GetValueEditor().Validators[0] is DelimitedValueValidator);
var validator = manifest.ParameterEditors[0].GetValueEditor().Validators[0] as DelimitedValueValidator;
Assert.IsNotNull(validator.Configuration);
Assert.AreEqual(",", validator.Configuration.Delimiter);
Assert.AreEqual("^[a-zA-Z]*$", validator.Configuration.Pattern);
}
[Test]
public void CanParseComments()
{
const string json1 = @"
// this is a single-line comment
{
""x"": 2, // this is an end-of-line comment
""y"": 3, /* this is a single line comment block
/* comment */ ""z"": /* comment */ 4,
""t"": ""this is /* comment */ a string"",
""u"": ""this is // more comment in a string""
}
";
var jobject = (JObject)JsonConvert.DeserializeObject(json1);
Assert.AreEqual("2", jobject.Property("x").Value.ToString());
Assert.AreEqual("3", jobject.Property("y").Value.ToString());
Assert.AreEqual("4", jobject.Property("z").Value.ToString());
Assert.AreEqual("this is /* comment */ a string", jobject.Property("t").Value.ToString());
Assert.AreEqual("this is // more comment in a string", jobject.Property("u").Value.ToString());
}
[Test]
public void ThrowOnJsonError()
{
// invalid json, missing the final ']' on javascript
const string json = @"{
propertyEditors: []/*we have empty property editors**/,
javascript: ['~/test.js',/*** some note about stuff asd09823-4**09234*/ '~/test2.js' }";
// parsing fails
Assert.Throws<JsonReaderException>(() => _parser.ParseManifest(json));
}
[Test]
public void CanParseManifest_ScriptsAndStylesheets()
{
string json = "{}";
PackageManifest manifest = _parser.ParseManifest(json);
Assert.AreEqual(0, manifest.Scripts.Length);
json = "{javascript: []}";
manifest = _parser.ParseManifest(json);
Assert.AreEqual(0, manifest.Scripts.Length);
json = "{javascript: ['~/test.js', '~/test2.js']}";
manifest = _parser.ParseManifest(json);
Assert.AreEqual(2, manifest.Scripts.Length);
json = "{propertyEditors: [], javascript: ['~/test.js', '~/test2.js']}";
manifest = _parser.ParseManifest(json);
Assert.AreEqual(2, manifest.Scripts.Length);
Assert.AreEqual(_ioHelper.ResolveUrl("/test.js"), manifest.Scripts[0]);
Assert.AreEqual(_ioHelper.ResolveUrl("/test2.js"), manifest.Scripts[1]);
// kludge is gone - must filter before parsing
json = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()) + "{propertyEditors: [], javascript: ['~/test.js', '~/test2.js']}";
Assert.Throws<JsonReaderException>(() => _parser.ParseManifest(json));
json = "{}";
manifest = _parser.ParseManifest(json);
Assert.AreEqual(0, manifest.Stylesheets.Length);
json = "{css: []}";
manifest = _parser.ParseManifest(json);
Assert.AreEqual(0, manifest.Stylesheets.Length);
json = "{css: ['~/style.css', '~/folder-name/sdsdsd/stylesheet.css']}";
manifest = _parser.ParseManifest(json);
Assert.AreEqual(2, manifest.Stylesheets.Length);
json = "{propertyEditors: [], css: ['~/stylesheet.css', '~/random-long-name.css']}";
manifest = _parser.ParseManifest(json);
Assert.AreEqual(2, manifest.Stylesheets.Length);
json = "{propertyEditors: [], javascript: ['~/test.js', '~/test2.js'], css: ['~/stylesheet.css', '~/random-long-name.css']}";
manifest = _parser.ParseManifest(json);
Assert.AreEqual(2, manifest.Scripts.Length);
Assert.AreEqual(2, manifest.Stylesheets.Length);
}
[Test]
public void CanParseManifest_PropertyEditors()
{
const string json = @"{'propertyEditors': [
{
alias: 'Test.Test1',
name: 'Test 1',
editor: {
view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html',
valueType: 'int',
hideLabel: true,
validation: {
'required': true,
'Regex': '\\d*'
}
},
prevalues: {
fields: [
{
label: 'Some config 1',
key: 'key1',
view: '~/App_Plugins/MyPackage/PropertyEditors/Views/pre-val1.html',
validation: {
required: true
}
},
{
label: 'Some config 2',
key: 'key2',
view: '~/App_Plugins/MyPackage/PropertyEditors/Views/pre-val2.html'
}
]
}
},
{
alias: 'Test.Test2',
name: 'Test 2',
isParameterEditor: true,
defaultConfig: { key1: 'some default val' },
editor: {
view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html',
valueType: 'int',
validation: {
required : true,
regex : '\\d*'
}
}
}
]}";
PackageManifest manifest = _parser.ParseManifest(json);
Assert.AreEqual(2, manifest.PropertyEditors.Length);
IDataEditor editor = manifest.PropertyEditors[1];
Assert.IsTrue((editor.Type & EditorType.MacroParameter) > 0);
Assert.IsNotEmpty(editor.DefaultConfiguration);
Assert.AreEqual("some default val", editor.DefaultConfiguration["key1"]);
editor = manifest.PropertyEditors[0];
Assert.AreEqual("Test.Test1", editor.Alias);
Assert.AreEqual("Test 1", editor.Name);
Assert.IsFalse((editor.Type & EditorType.MacroParameter) > 0);
IDataValueEditor valueEditor = editor.GetValueEditor();
Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/PropertyEditors/MyEditor.html"), valueEditor.View);
Assert.AreEqual("int", valueEditor.ValueType);
Assert.IsTrue(valueEditor.HideLabel);
// these two don't make much sense here
//// valueEditor.RegexValidator;
//// valueEditor.RequiredValidator;
List<IValueValidator> validators = valueEditor.Validators;
Assert.AreEqual(2, validators.Count);
IValueValidator validator = validators[0];
var v1 = validator as RequiredValidator;
Assert.IsNotNull(v1);
Assert.AreEqual("Required", v1.ValidationName);
validator = validators[1];
var v2 = validator as RegexValidator;
Assert.IsNotNull(v2);
Assert.AreEqual("Regex", v2.ValidationName);
Assert.AreEqual("\\d*", v2.Configuration);
// this is not part of the manifest
IDictionary<string, object> preValues = editor.GetConfigurationEditor().DefaultConfiguration;
Assert.IsEmpty(preValues);
IConfigurationEditor preValueEditor = editor.GetConfigurationEditor();
Assert.IsNotNull(preValueEditor);
Assert.IsNotNull(preValueEditor.Fields);
Assert.AreEqual(2, preValueEditor.Fields.Count);
ConfigurationField f = preValueEditor.Fields[0];
Assert.AreEqual("key1", f.Key);
Assert.AreEqual("Some config 1", f.Name);
Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/PropertyEditors/Views/pre-val1.html"), f.View);
List<IValueValidator> fvalidators = f.Validators;
Assert.IsNotNull(fvalidators);
Assert.AreEqual(1, fvalidators.Count);
var fv = fvalidators[0] as RequiredValidator;
Assert.IsNotNull(fv);
Assert.AreEqual("Required", fv.ValidationName);
f = preValueEditor.Fields[1];
Assert.AreEqual("key2", f.Key);
Assert.AreEqual("Some config 2", f.Name);
Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/PropertyEditors/Views/pre-val2.html"), f.View);
fvalidators = f.Validators;
Assert.IsNotNull(fvalidators);
Assert.AreEqual(0, fvalidators.Count);
}
[Test]
public void CanParseManifest_ParameterEditors()
{
const string json = @"{'parameterEditors': [
{
alias: 'parameter1',
name: 'My Parameter',
view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html'
},
{
alias: 'parameter2',
name: 'Another parameter',
config: { key1: 'some config val' },
view: '~/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html'
},
{
alias: 'parameter3',
name: 'Yet another parameter'
}
]}";
PackageManifest manifest = _parser.ParseManifest(json);
Assert.AreEqual(3, manifest.ParameterEditors.Length);
Assert.IsTrue(manifest.ParameterEditors.All(x => (x.Type & EditorType.MacroParameter) > 0));
IDataEditor editor = manifest.ParameterEditors[1];
Assert.AreEqual("parameter2", editor.Alias);
Assert.AreEqual("Another parameter", editor.Name);
IDictionary<string, object> config = editor.DefaultConfiguration;
Assert.AreEqual(1, config.Count);
Assert.IsTrue(config.ContainsKey("key1"));
Assert.AreEqual("some config val", config["key1"]);
IDataValueEditor valueEditor = editor.GetValueEditor();
Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html"), valueEditor.View);
editor = manifest.ParameterEditors[2];
Assert.Throws<InvalidOperationException>(() =>
{
IDataValueEditor valueEditor = editor.GetValueEditor();
});
}
[Test]
public void CanParseManifest_GridEditors()
{
const string json = @"{
'javascript': [ ],
'css': [ ],
'gridEditors': [
{
'name': 'Small Hero',
'alias': 'small-hero',
'view': '~/App_Plugins/MyPlugin/small-hero/editortemplate.html',
'render': '~/Views/Partials/Grid/Editors/SmallHero.cshtml',
'icon': 'icon-presentation',
'config': {
'image': {
'size': {
'width': 1200,
'height': 185
}
},
'link': {
'maxNumberOfItems': 1,
'minNumberOfItems': 0
}
}
},
{
'name': 'Document Links By Category',
'alias': 'document-links-by-category',
'view': '~/App_Plugins/MyPlugin/document-links-by-category/editortemplate.html',
'render': '~/Views/Partials/Grid/Editors/DocumentLinksByCategory.cshtml',
'icon': 'icon-umb-members'
}
]
}";
PackageManifest manifest = _parser.ParseManifest(json);
Assert.AreEqual(2, manifest.GridEditors.Length);
GridEditor editor = manifest.GridEditors[0];
Assert.AreEqual("small-hero", editor.Alias);
Assert.AreEqual("Small Hero", editor.Name);
Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPlugin/small-hero/editortemplate.html"), editor.View);
Assert.AreEqual(_ioHelper.ResolveUrl("/Views/Partials/Grid/Editors/SmallHero.cshtml"), editor.Render);
Assert.AreEqual("icon-presentation", editor.Icon);
IDictionary<string, object> config = editor.Config;
Assert.AreEqual(2, config.Count);
Assert.IsTrue(config.ContainsKey("image"));
object c = config["image"];
Assert.IsInstanceOf<JObject>(c); // FIXME: is this what we want?
Assert.IsTrue(config.ContainsKey("link"));
c = config["link"];
Assert.IsInstanceOf<JObject>(c); // FIXME: is this what we want?
// FIXME: should we resolveUrl in configs?
}
[Test]
public void CanParseManifest_ContentApps()
{
const string json = @"{'contentApps': [
{
alias: 'myPackageApp1',
name: 'My App1',
icon: 'icon-foo',
view: '~/App_Plugins/MyPackage/ContentApps/MyApp1.html'
},
{
alias: 'myPackageApp2',
name: 'My App2',
config: { key1: 'some config val' },
icon: 'icon-bar',
view: '~/App_Plugins/MyPackage/ContentApps/MyApp2.html'
}
]}";
PackageManifest manifest = _parser.ParseManifest(json);
Assert.AreEqual(2, manifest.ContentApps.Length);
Assert.IsInstanceOf<ManifestContentAppDefinition>(manifest.ContentApps[0]);
var app0 = (ManifestContentAppDefinition)manifest.ContentApps[0];
Assert.AreEqual("myPackageApp1", app0.Alias);
Assert.AreEqual("My App1", app0.Name);
Assert.AreEqual("icon-foo", app0.Icon);
Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/ContentApps/MyApp1.html"), app0.View);
Assert.IsInstanceOf<ManifestContentAppDefinition>(manifest.ContentApps[1]);
var app1 = (ManifestContentAppDefinition)manifest.ContentApps[1];
Assert.AreEqual("myPackageApp2", app1.Alias);
Assert.AreEqual("My App2", app1.Name);
Assert.AreEqual("icon-bar", app1.Icon);
Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/ContentApps/MyApp2.html"), app1.View);
}
[Test]
public void CanParseManifest_Dashboards()
{
const string json = @"{'dashboards': [
{
'alias': 'something',
'view': '~/App_Plugins/MyPackage/Dashboards/one.html',
'sections': [ 'content' ],
'access': [ {'grant':'user'}, {'deny':'foo'} ]
},
{
'alias': 'something.else',
'weight': -1,
'view': '~/App_Plugins/MyPackage/Dashboards/two.html',
'sections': [ 'forms' ],
}
]}";
PackageManifest manifest = _parser.ParseManifest(json);
Assert.AreEqual(2, manifest.Dashboards.Length);
Assert.IsInstanceOf<ManifestDashboard>(manifest.Dashboards[0]);
ManifestDashboard db0 = manifest.Dashboards[0];
Assert.AreEqual("something", db0.Alias);
Assert.AreEqual(100, db0.Weight);
Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/Dashboards/one.html"), db0.View);
Assert.AreEqual(1, db0.Sections.Length);
Assert.AreEqual("content", db0.Sections[0]);
Assert.AreEqual(2, db0.AccessRules.Length);
Assert.AreEqual(AccessRuleType.Grant, db0.AccessRules[0].Type);
Assert.AreEqual("user", db0.AccessRules[0].Value);
Assert.AreEqual(AccessRuleType.Deny, db0.AccessRules[1].Type);
Assert.AreEqual("foo", db0.AccessRules[1].Value);
Assert.IsInstanceOf<ManifestDashboard>(manifest.Dashboards[1]);
ManifestDashboard db1 = manifest.Dashboards[1];
Assert.AreEqual("something.else", db1.Alias);
Assert.AreEqual(-1, db1.Weight);
Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/Dashboards/two.html"), db1.View);
Assert.AreEqual(1, db1.Sections.Length);
Assert.AreEqual("forms", db1.Sections[0]);
}
[Test]
public void CanParseManifest_Sections()
{
const string json = @"{'sections': [
{ ""alias"": ""content"", ""name"": ""Content"" },
{ ""alias"": ""hello"", ""name"": ""World"" }
]}";
PackageManifest manifest = _parser.ParseManifest(json);
Assert.AreEqual(2, manifest.Sections.Length);
Assert.AreEqual("content", manifest.Sections[0].Alias);
Assert.AreEqual("hello", manifest.Sections[1].Alias);
Assert.AreEqual("Content", manifest.Sections[0].Name);
Assert.AreEqual("World", manifest.Sections[1].Name);
}
}
}