using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.DeliveryApi; using Umbraco.Cms.Core.Models.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Infrastructure.DeliveryApi; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.DeliveryApi; [TestFixture] public class ApiRichTextMarkupParserTests { private Mock _apiContentRouteBuilder; private Mock _apiMediaUrlProvider; [Test] public void Can_Parse_Legacy_LocalLinks() { var key1 = Guid.Parse("a1c5d649977f4ea59b1cb26055f3eed3"); var data1 = new MockData() .WithKey(key1) .WithRoutePath("/inline/") .WithRouteStartPath("inline"); var mockData = new Dictionary { { key1, data1 }, }; var parser = BuildDefaultSut(mockData); var legacyHtml = "

link to another page

"; var expectedOutput = "

link to another page

"; var parsedHtml = parser.Parse(legacyHtml); Assert.AreEqual(expectedOutput, parsedHtml); } [Test] public void Can_Parse_LocalLinks() { var key1 = Guid.Parse("eed5fc6b-96fd-45a5-a0f1-b1adfb483c2f"); var data1 = new MockData() .WithKey(key1) .WithRoutePath("/self/") .WithRouteStartPath("self"); var key2 = Guid.Parse("cc143afe-4cbf-46e5-b399-c9f451384373"); var data2 = new MockData() .WithKey(key2) .WithRoutePath("/other/") .WithRouteStartPath("other"); var mockData = new Dictionary { { key1, data1 }, { key2, data2 }, }; var parser = BuildDefaultSut(mockData); var html = @"

Rich text outside of the blocks with a link to itself

and to the other page

"; var expectedOutput = @"

Rich text outside of the blocks with a link to itself

and to the other page

"; var parsedHtml = parser.Parse(html); Assert.AreEqual(expectedOutput, parsedHtml); } [TestCase("#some-anchor")] [TestCase("?something=true")] [TestCase("#!some-hashbang")] [TestCase("?something=true#some-anchor")] public void Can_Parse_LocalLinks_With_Postfix(string postfix) { var key1 = Guid.Parse("eed5fc6b-96fd-45a5-a0f1-b1adfb483c2f"); var data1 = new MockData() .WithKey(key1) .WithRoutePath($"/self/{postfix}") .WithRouteStartPath("self"); var key2 = Guid.Parse("cc143afe-4cbf-46e5-b399-c9f451384373"); var data2 = new MockData() .WithKey(key2) .WithRoutePath($"/other/{postfix}") .WithRouteStartPath("other"); var mockData = new Dictionary { { key1, data1 }, { key2, data2 }, }; var parser = BuildDefaultSut(mockData); var html = $@"

Rich text outside of the blocks with a link to itself

and to the other page

"; var expectedOutput = $@"

Rich text outside of the blocks with a link to itself

and to the other page

"; var parsedHtml = parser.Parse(html); Assert.AreEqual(expectedOutput, parsedHtml); } [Test] public void Can_Parse_Inline_LocalImages() { var key1 = Guid.Parse("395bdc0e8f4d4ad4af7f3a3f6265651e"); var data1 = new MockData() .WithKey(key1) .WithMediaUrl("https://localhost:44331/media/bdofwokn/77gtp8fbrxmgkefatp10aw.webp"); var mockData = new Dictionary { { key1, data1 }, }; var parser = BuildDefaultSut(mockData); var legacyHtml = @"

An image

\n

"; var expectedOutput = @"

An image

\n

"; var parsedHtml = parser.Parse(legacyHtml); Assert.AreEqual(expectedOutput, parsedHtml); } private ApiRichTextMarkupParser BuildDefaultSut(Dictionary mockData) { var contentCacheMock = new Mock(); contentCacheMock.Setup(cc => cc.GetById(It.IsAny(), It.IsAny())) .Returns((preview, key) => mockData[key].PublishedContent); contentCacheMock.Setup(cc => cc.GetById(It.IsAny())) .Returns(key => mockData[key].PublishedContent); var mediaCacheMock = new Mock(); mediaCacheMock.Setup(cc => cc.GetById(It.IsAny(), It.IsAny())) .Returns((preview, key) => mockData[key].PublishedContent); mediaCacheMock.Setup(cc => cc.GetById(It.IsAny())) .Returns(key => mockData[key].PublishedContent); _apiMediaUrlProvider = new Mock(); _apiMediaUrlProvider.Setup(mup => mup.GetUrl(It.IsAny())) .Returns(ipc => mockData[ipc.Key].MediaUrl); _apiContentRouteBuilder = new Mock(); _apiContentRouteBuilder.Setup(acrb => acrb.Build(It.IsAny(), It.IsAny())) .Returns((content, culture) => mockData[content.Key].ApiContentRoute); return new ApiRichTextMarkupParser( _apiContentRouteBuilder.Object, _apiMediaUrlProvider.Object, contentCacheMock.Object, mediaCacheMock.Object, Mock.Of>()); } private class MockData { private Mock _publishedContentMock = new Mock(); private Mock _apiContentRouteMock = new Mock(); private Mock _apiContentStartItem = new Mock(); public IPublishedContent PublishedContent => _publishedContentMock.Object; public IApiContentRoute ApiContentRoute => _apiContentRouteMock.Object; public string MediaUrl { get; set; } = string.Empty; public MockData() { _apiContentRouteMock.SetupGet(r => r.StartItem).Returns(_apiContentStartItem.Object); } public MockData WithKey(Guid key) { _publishedContentMock.SetupGet(i => i.Key).Returns(key); _apiContentStartItem.SetupGet(rsi => rsi.Id).Returns(key); return this; } public MockData WithRoutePath(string path) { _apiContentRouteMock.SetupGet(r => r.Path).Returns(path); return this; } public MockData WithRouteStartPath(string path) { _apiContentStartItem.SetupGet(rsi => rsi.Path).Returns(path); return this; } public MockData WithMediaUrl(string url) { MediaUrl = url; return this; } } }