From e7f40affacebb3d819d8327c025a3428f8b8e1a5 Mon Sep 17 00:00:00 2001 From: Andreas Zerbst <73799582+andr317c@users.noreply.github.com> Date: Tue, 19 Mar 2024 13:14:38 +0100 Subject: [PATCH] V14 Bugfix ensures correct line endings for partial view snippets (#15906) * Created extension class so we can ensure native line endings * Added usage of extension method for ensuring native line endings * Added tests, to see if the snippets return the correct content * Removed space --- .../Extensions/LineEndingsExtensions.cs | 50 +++++++++++++++++++ .../PartialViewSnippetCollectionBuilder.cs | 2 + .../Settings/PartialView/PartialView.spec.ts | 19 +++++-- 3 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Core/Extensions/LineEndingsExtensions.cs diff --git a/src/Umbraco.Core/Extensions/LineEndingsExtensions.cs b/src/Umbraco.Core/Extensions/LineEndingsExtensions.cs new file mode 100644 index 0000000000..f074c27776 --- /dev/null +++ b/src/Umbraco.Core/Extensions/LineEndingsExtensions.cs @@ -0,0 +1,50 @@ +using System.Runtime.InteropServices; + +namespace Umbraco.Cms.Core.Extensions; + +public static class LineEndingsExtensions +{ + /// + /// Ensures Lf only everywhere. + /// + /// The text to filter. + /// The filtered text. + private static string Lf(string text) + { + if (string.IsNullOrEmpty(text)) + { + return text; + } + + text = text.Replace("\r", string.Empty); // remove CR + return text; + } + + /// + /// Ensures CrLf everywhere. + /// + /// The text to filter. + /// The filtered text. + private static string CrLf(string text) + { + if (string.IsNullOrEmpty(text)) + { + return text; + } + + text = text.Replace("\r", string.Empty); // remove CR + text = text.Replace("\n", "\r\n"); // add CRLF everywhere + return text; + } + + /// + /// Ensures native line endings. + /// + /// the text to ensure native line endings for. + /// the text with native line endings. + public static string EnsureNativeLineEndings(this string text) + { + var useCrLf = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + return useCrLf ? CrLf(text) : Lf(text); + } +} diff --git a/src/Umbraco.Core/Snippets/PartialViewSnippetCollectionBuilder.cs b/src/Umbraco.Core/Snippets/PartialViewSnippetCollectionBuilder.cs index 31f33cf6f2..9e9746afe4 100644 --- a/src/Umbraco.Core/Snippets/PartialViewSnippetCollectionBuilder.cs +++ b/src/Umbraco.Core/Snippets/PartialViewSnippetCollectionBuilder.cs @@ -2,6 +2,7 @@ using System.Text.RegularExpressions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Extensions; using Umbraco.Cms.Core.Strings; using Umbraco.Extensions; @@ -38,6 +39,7 @@ public partial class PartialViewSnippetCollectionBuilder : LazyCollectionBuilder private string CleanUpSnippetContent(string content) { const string partialViewHeader = "@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage"; + content = content.EnsureNativeLineEndings(); // Strip the @inherits if it's there Regex headerMatch = HeaderRegex(); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/PartialView/PartialView.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/PartialView/PartialView.spec.ts index 74f539e2cf..18afe4842a 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/PartialView/PartialView.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/PartialView/PartialView.spec.ts @@ -33,9 +33,10 @@ test.describe('Partial View tests', () => { await expect(umbracoUi.partialView.checkItemNameUnderPartialViewTree(partialViewFileName)).toBeVisible(); }) - test.skip('can create a partial view from snippet', async ({umbracoApi, umbracoUi}) => { + test('can create a partial view from snippet', async ({umbracoApi, umbracoUi}) => { // Arrange - const expectedPartialViewContent = '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage\n@using Umbraco.Cms.Core.Routing\n@using Umbraco.Extensions\n\n@inject IPublishedUrlProvider PublishedUrlProvider\n@*\n This snippet makes a breadcrumb of parents using an unordered HTML list.\n\n How it works:\n - It uses the Ancestors() method to get all parents and then generates links so the visitor can go back\n - Finally it outputs the name of the current page (without a link)\n*@\n\n@{ var selection = Model.Ancestors().ToArray(); }\n\n@if (selection?.Length > 0)\n{\n \n}'; + const expectedPartialViewContentWindows = '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage\r\n@using Umbraco.Cms.Core.Routing\r\n@using Umbraco.Extensions\r\n\n@inject IPublishedUrlProvider PublishedUrlProvider\r\n@*\r\n This snippet makes a breadcrumb of parents using an unordered HTML list.\r\n\r\n How it works:\r\n - It uses the Ancestors() method to get all parents and then generates links so the visitor can go back\r\n - Finally it outputs the name of the current page (without a link)\r\n*@\r\n\r\n@{ var selection = Model.Ancestors().ToArray(); }\r\n\r\n@if (selection?.Length > 0)\r\n{\r\n \r\n}'; + const expectedPartialViewContentLinux = '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage\n@using Umbraco.Cms.Core.Routing\n@using Umbraco.Extensions\n\n@inject IPublishedUrlProvider PublishedUrlProvider\n@*\n This snippet makes a breadcrumb of parents using an unordered HTML list.\n\n How it works:\n - It uses the Ancestors() method to get all parents and then generates links so the visitor can go back\n - Finally it outputs the name of the current page (without a link)\n*@\n\n@{ var selection = Model.Ancestors().ToArray(); }\n\n@if (selection?.Length > 0)\n{\n \n}'; // Act await umbracoUi.partialView.clickActionsMenuAtRoot(); @@ -50,8 +51,18 @@ test.describe('Partial View tests', () => { await umbracoUi.partialView.isSuccessNotificationVisible(); expect(await umbracoApi.partialView.doesExist(partialViewFileName)).toBeTruthy(); const partialViewData = await umbracoApi.partialView.getByName(partialViewFileName); - expect(partialViewData.content.length).toBe(expectedPartialViewContent.length); - expect(partialViewData.content).toBe(expectedPartialViewContent); + + switch (process.platform) { + case 'win32': + expect(partialViewData.content).toBe(expectedPartialViewContentWindows); + break; + case 'linux': + expect(partialViewData.content).toBe(expectedPartialViewContentLinux); + break; + default: + throw new Error(`Untested platform: ${process.platform}`); + } + // Verify the new partial view is displayed under the Partial Views section await umbracoUi.partialView.clickRootFolderCaretButton(); await expect(umbracoUi.partialView.checkItemNameUnderPartialViewTree(partialViewFileName)).toBeVisible();