diff --git a/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs b/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs index d7645ec434..b04889a842 100644 --- a/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs +++ b/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs @@ -8,6 +8,8 @@ using Moq; using Umbraco.Core.BackOffice; using Umbraco.Tests.Common.Builders; using Umbraco.Web.BackOffice.Controllers; +using Umbraco.Core; +using Umbraco.Web.Common.Install; namespace Umbraco.Tests.UnitTests.AutoFixture { @@ -36,12 +38,26 @@ namespace Umbraco.Tests.UnitTests.AutoFixture (a,b,c) => BackOfficeIdentityUser.CreateNew(new GlobalSettingsBuilder().Build(),a,b,c))); fixture .Customize(new ConstructorCustomization(typeof(UsersController), new GreedyConstructorQuery())) + .Customize(new ConstructorCustomization(typeof(InstallController), new GreedyConstructorQuery())) + .Customize(new ConstructorCustomization(typeof(PreviewController), new GreedyConstructorQuery())) + .Customize(new ConstructorCustomization(typeof(BackOfficeController), new GreedyConstructorQuery())) .Customize(new ConstructorCustomization(typeof(BackOfficeUserManager), new GreedyConstructorQuery())) .Customize(new AutoMoqCustomization()); // When requesting an IUserStore ensure we actually uses a IUserLockoutStore fixture.Customize>(cc => cc.FromFactory(() => Mock.Of>())); + fixture.Customize( + u => u.FromFactory( + (a, b, c) => new ConfigConnectionString(a, b, c))); + + fixture.Customize( + u => u.FromFactory( + () => new UmbracoVersion())); + + var connectionStrings = Mock.Of(); + Mock.Get(connectionStrings).Setup(x => x[Constants.System.UmbracoConnectionName]).Returns((ConfigConnectionString)new ConfigConnectionString(string.Empty, string.Empty, string.Empty)); + fixture.Customize(x => x.FromFactory(() => connectionStrings )); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/FileNameTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/FileNameTests.cs new file mode 100644 index 0000000000..c1c331903e --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/FileNameTests.cs @@ -0,0 +1,103 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using AutoFixture.NUnit3; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Configuration; +using Umbraco.Core.Hosting; +using Umbraco.Tests.UnitTests.AutoFixture; +using Umbraco.Web.BackOffice.Controllers; +using Umbraco.Web.Common.Install; + +namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common +{ + [TestFixture] + internal class FileNameTests + { + private string GetViewName(ViewResult viewResult, string separator = "/") + { + var sections = viewResult.ViewName.Split(separator); + return sections[^1]; + } + + private IEnumerable GetUiFiles(IEnumerable pathFromNetCore) + { + var sourceRoot = TestContext.CurrentContext.TestDirectory.Split("Umbraco.Tests.UnitTests")[0]; + var pathToFiles = Path.Combine(sourceRoot, "Umbraco.Web.UI.NetCore"); + foreach (var pathSection in pathFromNetCore) + { + pathToFiles = Path.Combine(pathToFiles, pathSection); + } + + return new DirectoryInfo(pathToFiles).GetFiles().Select(f => f.Name).ToArray(); + } + + [Test] + [AutoMoqData] + public async Task InstallViewExists( + [Frozen] IHostingEnvironment hostingEnvironment, + InstallController sut) + { + Mock.Get(hostingEnvironment).Setup(x => x.ToAbsolute(It.IsAny())).Returns("http://localhost/"); + var viewResult = await sut.Index() as ViewResult; + var fileName = GetViewName(viewResult, Path.DirectorySeparatorChar.ToString()); + + var views = GetUiFiles(new[] { "Umbraco", "UmbracoInstall" }); + Assert.True(views.Contains(fileName), $"Expected {fileName} to exist, but it didn't"); + } + + [Test] + [AutoMoqData] + public void PreviewViewExists( + [Frozen] IGlobalSettings globalSettings, + PreviewController sut) + { + Mock.Get(globalSettings).Setup(x => x.UmbracoPath).Returns("/"); + + var viewResult = sut.Index() as ViewResult; + var fileName = GetViewName(viewResult); + + var views = GetUiFiles(new[] { "Umbraco", "UmbracoBackOffice" }); + + Assert.True(views.Contains(fileName), $"Expected {fileName} to exist, but it didn't"); + } + + [Test] + [AutoMoqData] + public async Task BackOfficeDefaultExists( + [Frozen] IGlobalSettings globalSettings, + [Frozen] IHostingEnvironment hostingEnvironment, + [Frozen] ITempDataDictionary tempDataDictionary, + BackOfficeController sut) + { + Mock.Get(globalSettings).Setup(x => x.UmbracoPath).Returns("/"); + Mock.Get(hostingEnvironment).Setup(x => x.ToAbsolute("/")).Returns("http://localhost/"); + Mock.Get(hostingEnvironment).SetupGet(x => x.ApplicationVirtualPath).Returns("/"); + + + sut.TempData = tempDataDictionary; + + var viewResult = await sut.Default() as ViewResult; + var fileName = GetViewName(viewResult); + var views = GetUiFiles(new[] { "Umbraco", "UmbracoBackOffice" }); + + Assert.True(views.Contains(fileName), $"Expected {fileName} to exist, but it didn't"); + } + + + [Test] + public void LanguageFilesAreLowercase() + { + var files = GetUiFiles(new[] { "Umbraco", "config", "lang" }); + foreach (var fileName in files) + { + Assert.AreEqual(fileName.ToLower(), fileName, + $"Language files must be all lowercase but {fileName} is not lowercase."); + } + } + } +} diff --git a/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs b/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs index dbfbee912e..31ff07b964 100644 --- a/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs @@ -47,6 +47,16 @@ namespace Umbraco.Extensions return linkGenerator.GetPathByAction(nameof(InstallController.Index), ControllerExtensions.GetControllerName(), new { area = Constants.Web.Mvc.InstallArea }); } + /// + /// Returns the URL for the installer api + /// + /// + /// + public static string GetInstallerApiUrl(this LinkGenerator linkGenerator) + { + return linkGenerator.GetPathByAction(nameof(InstallController.Index), ControllerExtensions.GetControllerName(), new { area = Constants.Web.Mvc.InstallArea }).EnsureEndsWith('/'); + } + /// /// Return the Url for a Web Api service /// diff --git a/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs b/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs index 8b1d41634d..03329547bc 100644 --- a/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs @@ -32,18 +32,6 @@ namespace Umbraco.Extensions return url.Action("Default", ControllerExtensions.GetControllerName(backOfficeControllerType), new { area = Constants.Web.Mvc.BackOfficeApiArea }); } - /// - /// Return the installer API url - /// - /// - /// - public static string GetInstallerApiUrl(this IUrlHelper url) - { - // there is no default action here so we need to get it by action and trim the action - return url.Action("GetSetup", ControllerExtensions.GetControllerName(), new { area = Constants.Web.Mvc.InstallArea }) - .TrimEnd("GetSetup"); - } - /// /// Return the Url for a Web Api service /// diff --git a/src/Umbraco.Web.Common/Install/InstallController.cs b/src/Umbraco.Web.Common/Install/InstallController.cs index b5da8eabd4..306a376533 100644 --- a/src/Umbraco.Web.Common/Install/InstallController.cs +++ b/src/Umbraco.Web.Common/Install/InstallController.cs @@ -83,7 +83,7 @@ namespace Umbraco.Web.Common.Install } // gen the install base url - ViewData.SetInstallApiBaseUrl(Url.GetInstallerApiUrl()); + ViewData.SetInstallApiBaseUrl(_linkGenerator.GetInstallerApiUrl()); // get the base umbraco folder var baseFolder = _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoPath); diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json index ee67d0cf58..2044a8e696 100644 --- a/src/Umbraco.Web.UI.NetCore/appsettings.json +++ b/src/Umbraco.Web.UI.NetCore/appsettings.json @@ -1,60 +1,60 @@ { - "ConnectionStrings": { + "ConnectionStrings": { "umbracoDbDSN": "" - }, - "Umbraco": { - "CMS": { - "Content": { - "Notifications": { - "Email": "your@email.here" - }, - "MacroErrors": "throw" - }, - "Global": { - "DefaultUILanguage": "en-us", - "HideTopLevelNodeFromPath": true, - "Path": "~/umbraco", - "TimeOutInMinutes": 20, - "UseHttps": false - }, - "Hosting": { - "Debug": false - }, - "KeepAlive": { - "DisableKeepAliveTask": false, - "KeepAlivePingUrl": "{umbracoApplicationUrl}/api/keepalive/ping" - }, - "RequestHandler": { - "ConvertUrlsToAscii": "try" - }, - "RuntimeMinification": { - "dataFolder": "App_Data\\Smidge", - "version": "1" - }, - "Security": { - "KeepUserLoggedIn": false, - "UsernameIsEmail": true, - "HideDisabledUsersInBackoffice": false, - "UserPassword": { - "RequiredLength": 10, - "RequireNonLetterOrDigit": false, - "RequireDigit": false, - "RequireLowercase": false, - "RequireUppercase": false, - "MaxFailedAccessAttemptsBeforeLockout": 0 - }, - "MemberPassword": { - "RequiredLength": 10, - "RequireNonLetterOrDigit": false, - "RequireDigit": false, - "RequireLowercase": false, - "RequireUppercase": false, - "MaxFailedAccessAttemptsBeforeLockout": 0 - } - }, - "Tours": { - "EnableTours": true - } + }, + "Umbraco": { + "CMS": { + "Content": { + "Notifications": { + "Email": "your@email.here" + }, + "MacroErrors": "throw" + }, + "Global": { + "DefaultUILanguage": "en-us", + "HideTopLevelNodeFromPath": true, + "Path": "~/umbraco", + "TimeOutInMinutes": 20, + "UseHttps": false + }, + "Hosting": { + "Debug": false + }, + "KeepAlive": { + "DisableKeepAliveTask": false, + "KeepAlivePingUrl": "{umbracoApplicationUrl}/api/keepalive/ping" + }, + "RequestHandler": { + "ConvertUrlsToAscii": "try" + }, + "RuntimeMinification": { + "dataFolder": "App_Data\\Smidge", + "version": "1" + }, + "Security": { + "KeepUserLoggedIn": false, + "UsernameIsEmail": true, + "HideDisabledUsersInBackoffice": false, + "UserPassword": { + "RequiredLength": 10, + "RequireNonLetterOrDigit": false, + "RequireDigit": false, + "RequireLowercase": false, + "RequireUppercase": false, + "MaxFailedAccessAttemptsBeforeLockout": 5 + }, + "MemberPassword": { + "RequiredLength": 10, + "RequireNonLetterOrDigit": false, + "RequireDigit": false, + "RequireLowercase": false, + "RequireUppercase": false, + "MaxFailedAccessAttemptsBeforeLockout": 5 } + }, + "Tours": { + "EnableTours": true + } } + } } diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 8babcc703f..ce20dcf187 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -72,7 +72,7 @@ namespace Umbraco.Web.Editors //this is the filter for the keys that we'll keep based on the full version of the server vars var keepOnlyKeys = new Dictionary { - {"umbracoUrls", new[] {"authenticationApiBaseUrl", "serverVarsJs", "externalLoginsUrl", "currentUserApiBaseUrl"}}, + {"umbracoUrls", new[] {"authenticationApiBaseUrl", "serverVarsJs", "externalLoginsUrl", "currentUserApiBaseUrl", "iconApiBaseUrl"}}, {"umbracoSettings", new[] {"allowPasswordReset", "imageFileTypes", "maxFileSize", "loginBackgroundImage", "canSendRequiredEmail", "usernameIsEmail"}}, {"application", new[] {"applicationPath", "cacheBuster"}}, {"isDebuggingEnabled", new string[] { }},