From 1752be989db0ceb360c6e75d0101c831461fbfec Mon Sep 17 00:00:00 2001 From: Mole Date: Wed, 29 Jan 2025 13:59:58 +0100 Subject: [PATCH] V15: Fix Url Preview (#18072) * Make URL overview align with the old routing This means including custom url providers, other URLS, etc. * Move implementation to its own provider * Handle could not get url * Migrate intergration tests to new implementation --- .../Factories/DocumentUrlFactory.cs | 25 ++-- .../Factories/IDocumentUrlFactory.cs | 1 + .../DependencyInjection/UmbracoBuilder.cs | 1 + .../Routing/IPublishedUrlInfoProvider.cs | 13 ++ .../Routing/PublishedUrlInfoProvider.cs | 122 ++++++++++++++++++ .../Services/DocumentUrlService.cs | 1 + .../Services/DocumentUrlServiceTest.cs | 38 ------ ...cumentUrlServiceTest_hidetoplevel_false.cs | 29 ----- .../Services/PublishedUrlInfoProviderTests.cs | 43 ++++++ .../PublishedUrlInfoProviderTestsBase.cs | 62 +++++++++ ...ishedUrlInfoProvider_hidetoplevel_false.cs | 45 +++++++ .../Umbraco.Tests.Integration.csproj | 6 + 12 files changed, 310 insertions(+), 76 deletions(-) create mode 100644 src/Umbraco.Core/Routing/IPublishedUrlInfoProvider.cs create mode 100644 src/Umbraco.Core/Routing/PublishedUrlInfoProvider.cs create mode 100644 tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProviderTests.cs create mode 100644 tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProviderTestsBase.cs create mode 100644 tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProvider_hidetoplevel_false.cs diff --git a/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs index 6d0e40e2b1..ba6ebfaf9a 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs @@ -1,31 +1,38 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Umbraco.Cms.Api.Management.ViewModels.Document; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Navigation; -using Umbraco.Cms.Core.Web; -using Umbraco.Extensions; namespace Umbraco.Cms.Api.Management.Factories; public class DocumentUrlFactory : IDocumentUrlFactory { - private readonly IDocumentUrlService _documentUrlService; + private readonly IPublishedUrlInfoProvider _publishedUrlInfoProvider; + [Obsolete("Use the constructor that takes all dependencies, scheduled for removal in v16")] public DocumentUrlFactory(IDocumentUrlService documentUrlService) + : this(StaticServiceProvider.Instance.GetRequiredService()) { - _documentUrlService = documentUrlService; + } + + [Obsolete("Use the constructor that takes all dependencies, scheduled for removal in v16")] + public DocumentUrlFactory(IDocumentUrlService documentUrlService, IPublishedUrlInfoProvider publishedUrlInfoProvider) + : this(publishedUrlInfoProvider) + { + + } + + public DocumentUrlFactory(IPublishedUrlInfoProvider publishedUrlInfoProvider) + { + _publishedUrlInfoProvider = publishedUrlInfoProvider; } public async Task> CreateUrlsAsync(IContent content) { - IEnumerable urlInfos = await _documentUrlService.ListUrlsAsync(content.Key); + ISet urlInfos = await _publishedUrlInfoProvider.GetAllAsync(content); return urlInfos .Where(urlInfo => urlInfo.IsUrl) diff --git a/src/Umbraco.Cms.Api.Management/Factories/IDocumentUrlFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/IDocumentUrlFactory.cs index f87a0aaec8..a64d069704 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/IDocumentUrlFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/IDocumentUrlFactory.cs @@ -6,5 +6,6 @@ namespace Umbraco.Cms.Api.Management.Factories; public interface IDocumentUrlFactory { Task> CreateUrlsAsync(IContent content); + Task> CreateUrlSetsAsync(IEnumerable contentItems); } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 14878e5639..95c6da574a 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -241,6 +241,7 @@ namespace Umbraco.Cms.Core.DependencyInjection // register published router Services.AddUnique(); + Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); diff --git a/src/Umbraco.Core/Routing/IPublishedUrlInfoProvider.cs b/src/Umbraco.Core/Routing/IPublishedUrlInfoProvider.cs new file mode 100644 index 0000000000..aa3d322381 --- /dev/null +++ b/src/Umbraco.Core/Routing/IPublishedUrlInfoProvider.cs @@ -0,0 +1,13 @@ +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Core.Routing; + +public interface IPublishedUrlInfoProvider +{ + /// + /// Gets all published urls for a content item. + /// + /// The content to get urls for. + /// Set of all published url infos. + Task> GetAllAsync(IContent content); +} diff --git a/src/Umbraco.Core/Routing/PublishedUrlInfoProvider.cs b/src/Umbraco.Core/Routing/PublishedUrlInfoProvider.cs new file mode 100644 index 0000000000..d4059bcab8 --- /dev/null +++ b/src/Umbraco.Core/Routing/PublishedUrlInfoProvider.cs @@ -0,0 +1,122 @@ +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Web; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Routing; + +public class PublishedUrlInfoProvider : IPublishedUrlInfoProvider +{ + private readonly IPublishedUrlProvider _publishedUrlProvider; + private readonly ILanguageService _languageService; + private readonly IPublishedRouter _publishedRouter; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly ILocalizedTextService _localizedTextService; + private readonly ILogger _logger; + private readonly UriUtility _uriUtility; + private readonly IVariationContextAccessor _variationContextAccessor; + + public PublishedUrlInfoProvider( + IPublishedUrlProvider publishedUrlProvider, + ILanguageService languageService, + IPublishedRouter publishedRouter, + IUmbracoContextAccessor umbracoContextAccessor, + ILocalizedTextService localizedTextService, + ILogger logger, + UriUtility uriUtility, + IVariationContextAccessor variationContextAccessor) + { + _publishedUrlProvider = publishedUrlProvider; + _languageService = languageService; + _publishedRouter = publishedRouter; + _umbracoContextAccessor = umbracoContextAccessor; + _localizedTextService = localizedTextService; + _logger = logger; + _uriUtility = uriUtility; + _variationContextAccessor = variationContextAccessor; + } + + /// + public async Task> GetAllAsync(IContent content) + { + HashSet urlInfos = []; + var cultures = (await _languageService.GetAllAsync()).Select(x => x.IsoCode).ToArray(); + + // First we get the urls of all cultures, using the published router, meaning we respect any extensions. + foreach (var culture in cultures) + { + var url = _publishedUrlProvider.GetUrl(content.Key, culture: culture); + + // Handle "could not get URL" + if (url is "#" or "#ex") + { + urlInfos.Add(UrlInfo.Message(_localizedTextService.Localize("content", "getUrlException"), culture)); + continue; + } + + // Check for collision + Attempt hasCollision = await VerifyCollisionAsync(content, url, culture); + + if (hasCollision is { Success: true, Result: not null }) + { + urlInfos.Add(hasCollision.Result); + continue; + } + + urlInfos.Add(UrlInfo.Url(url, culture)); + } + + // Then get "other" urls - I.E. Not what you'd get with GetUrl(), this includes all the urls registered using domains. + // for these 'other' URLs, we don't check whether they are routable, collide, anything - we just report them. + foreach (UrlInfo otherUrl in _publishedUrlProvider.GetOtherUrls(content.Id).OrderBy(x => x.Text).ThenBy(x => x.Culture)) + { + urlInfos.Add(otherUrl); + } + + return urlInfos; + } + + private async Task> VerifyCollisionAsync(IContent content, string url, string culture) + { + var uri = new Uri(url.TrimEnd(Constants.CharArrays.ForwardSlash), UriKind.RelativeOrAbsolute); + if (uri.IsAbsoluteUri is false) + { + uri = uri.MakeAbsolute(_umbracoContextAccessor.GetRequiredUmbracoContext().CleanedUmbracoUrl); + } + + uri = _uriUtility.UriToUmbraco(uri); + IPublishedRequestBuilder builder = await _publishedRouter.CreateRequestAsync(uri); + IPublishedRequest publishedRequest = await _publishedRouter.RouteRequestAsync(builder, new RouteRequestOptions(RouteDirection.Outbound)); + + if (publishedRequest.HasPublishedContent() is false) + { + if (_logger.IsEnabled(LogLevel.Debug)) + { + const string logMsg = nameof(VerifyCollisionAsync) + + " did not resolve a content item for original url: {Url}, translated to {TranslatedUrl} and culture: {Culture}"; + _logger.LogDebug(logMsg, url, uri, culture); + } + + var urlInfo = UrlInfo.Message(_localizedTextService.Localize("content", "routeErrorCannotRoute"), culture); + return Attempt.Succeed(urlInfo); + } + + if (publishedRequest.IgnorePublishedContentCollisions) + { + return Attempt.Fail(); + } + + if (publishedRequest.PublishedContent?.Id != content.Id) + { + var collidingContent = publishedRequest.PublishedContent?.Key.ToString(); + + var urlInfo = UrlInfo.Message(_localizedTextService.Localize("content", "routeError", [collidingContent]), culture); + return Attempt.Succeed(urlInfo); + } + + // No collision + return Attempt.Fail(); + } +} diff --git a/src/Umbraco.Core/Services/DocumentUrlService.cs b/src/Umbraco.Core/Services/DocumentUrlService.cs index 4c99ba87e3..09c2e434ea 100644 --- a/src/Umbraco.Core/Services/DocumentUrlService.cs +++ b/src/Umbraco.Core/Services/DocumentUrlService.cs @@ -525,6 +525,7 @@ public class DocumentUrlService : IDocumentUrlService } + [Obsolete("This method is obsolete and will be removed in future versions. Use IPublishedUrlInfoProvider.GetAllAsync instead.")] public async Task> ListUrlsAsync(Guid contentKey) { var result = new List(); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest.cs index 46a983e435..500b7bdc0d 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest.cs @@ -213,44 +213,6 @@ public class DocumentUrlServiceTest : UmbracoIntegrationTestWithContent return DocumentUrlService.GetDocumentKeyByRoute(route, isoCode, null, loadDraft)?.ToString()?.ToUpper(); } - [Test] - public async Task Two_items_in_level_1_with_same_name_will_have_conflicting_routes() - { - // Create a second root - var secondRoot = ContentBuilder.CreateSimpleContent(ContentType, "Second Root", null); - var contentSchedule = ContentScheduleCollection.CreateWithEntry(DateTime.Now.AddMinutes(-5), null); - ContentService.Save(secondRoot, -1, contentSchedule); - - // Create a child of second root - var childOfSecondRoot = ContentBuilder.CreateSimpleContent(ContentType, Subpage.Name, secondRoot); - childOfSecondRoot.Key = new Guid("FF6654FB-BC68-4A65-8C6C-135567F50BD6"); - ContentService.Save(childOfSecondRoot, -1, contentSchedule); - - // Publish both the main root and the second root with descendants - ContentService.PublishBranch(Textpage, true, new[] { "*" }); - ContentService.PublishBranch(secondRoot, true, new[] { "*" }); - - var subPageUrls = await DocumentUrlService.ListUrlsAsync(Subpage.Key); - var childOfSecondRootUrls = await DocumentUrlService.ListUrlsAsync(childOfSecondRoot.Key); - - //Assert the url of subpage is correct - Assert.AreEqual(1, subPageUrls.Count()); - Assert.IsTrue(subPageUrls.First().IsUrl); - Assert.AreEqual("/text-page-1", subPageUrls.First().Text); - Assert.AreEqual(Subpage.Key, DocumentUrlService.GetDocumentKeyByRoute("/text-page-1", "en-US", null, false)); - - //Assert the url of child of second root is not exposed - Assert.AreEqual(1, childOfSecondRootUrls.Count()); - Assert.IsFalse(childOfSecondRootUrls.First().IsUrl); - - //Ensure the url without hide top level is not finding the child of second root - Assert.AreNotEqual(childOfSecondRoot.Key, DocumentUrlService.GetDocumentKeyByRoute("/second-root/text-page-1", "en-US", null, false)); - - - - } - - //TODO test cases: // - Find the root, when a domain is set diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest_hidetoplevel_false.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest_hidetoplevel_false.cs index 1944a114fc..41e3f18979 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest_hidetoplevel_false.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest_hidetoplevel_false.cs @@ -120,33 +120,4 @@ public class DocumentUrlServiceTest_HideTopLevel_False : UmbracoIntegrationTestW return DocumentUrlService.GetDocumentKeyByRoute(route, isoCode, null, loadDraft)?.ToString()?.ToUpper(); } - - [Test] - public async Task Two_items_in_level_1_with_same_name_will_not_have_conflicting_routes() - { - // Create a second root - var secondRoot = ContentBuilder.CreateSimpleContent(ContentType, "Second Root", null); - var contentSchedule = ContentScheduleCollection.CreateWithEntry(DateTime.Now.AddMinutes(-5), null); - ContentService.Save(secondRoot, -1, contentSchedule); - - // Create a child of second root - var childOfSecondRoot = ContentBuilder.CreateSimpleContent(ContentType, Subpage.Name, secondRoot); - childOfSecondRoot.Key = new Guid("FF6654FB-BC68-4A65-8C6C-135567F50BD6"); - ContentService.Save(childOfSecondRoot, -1, contentSchedule); - - // Publish both the main root and the second root with descendants - ContentService.PublishBranch(Textpage, true, new[] { "*" }); - ContentService.PublishBranch(secondRoot, true, new[] { "*" }); - - var subPageUrls = await DocumentUrlService.ListUrlsAsync(Subpage.Key); - var childOfSecondRootUrls = await DocumentUrlService.ListUrlsAsync(childOfSecondRoot.Key); - - Assert.AreEqual(1, subPageUrls.Count()); - Assert.IsTrue(subPageUrls.First().IsUrl); - Assert.AreEqual("/textpage/text-page-1", subPageUrls.First().Text); - - Assert.AreEqual(1, childOfSecondRootUrls.Count()); - Assert.IsTrue(childOfSecondRootUrls.First().IsUrl); - Assert.AreEqual("/second-root/text-page-1", childOfSecondRootUrls.First().Text); - } } diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProviderTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProviderTests.cs new file mode 100644 index 0000000000..0b73743b8a --- /dev/null +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProviderTests.cs @@ -0,0 +1,43 @@ +using NUnit.Framework; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Tests.Common.Builders; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services; + +public class PublishedUrlInfoProviderTests : PublishedUrlInfoProviderTestsBase +{ + + [Test] + public async Task Two_items_in_level_1_with_same_name_will_have_conflicting_routes() + { + // Create a second root + var secondRoot = ContentBuilder.CreateSimpleContent(ContentType, "Second Root", null); + var contentSchedule = ContentScheduleCollection.CreateWithEntry(DateTime.Now.AddMinutes(-5), null); + ContentService.Save(secondRoot, -1, contentSchedule); + + // Create a child of second root + var childOfSecondRoot = ContentBuilder.CreateSimpleContent(ContentType, Subpage.Name, secondRoot); + childOfSecondRoot.Key = new Guid("FF6654FB-BC68-4A65-8C6C-135567F50BD6"); + ContentService.Save(childOfSecondRoot, -1, contentSchedule); + + // Publish both the main root and the second root with descendants + ContentService.PublishBranch(Textpage, true, new[] { "*" }); + ContentService.PublishBranch(secondRoot, true, new[] { "*" }); + + var subPageUrls = await PublishedUrlInfoProvider.GetAllAsync(Subpage); + var childOfSecondRootUrls = await PublishedUrlInfoProvider.GetAllAsync(childOfSecondRoot); + + // Assert the url of subpage is correct + Assert.AreEqual(1, subPageUrls.Count); + Assert.IsTrue(subPageUrls.First().IsUrl); + Assert.AreEqual("/text-page-1/", subPageUrls.First().Text); + Assert.AreEqual(Subpage.Key, DocumentUrlService.GetDocumentKeyByRoute("/text-page-1/", "en-US", null, false)); + + // Assert the url of child of second root is not exposed + Assert.AreEqual(1, childOfSecondRootUrls.Count); + Assert.IsFalse(childOfSecondRootUrls.First().IsUrl); + + // Ensure the url without hide top level is not finding the child of second root + Assert.AreNotEqual(childOfSecondRoot.Key, DocumentUrlService.GetDocumentKeyByRoute("/second-root/text-page-1/", "en-US", null, false)); + } +} diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProviderTestsBase.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProviderTestsBase.cs new file mode 100644 index 0000000000..1a27d998af --- /dev/null +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProviderTestsBase.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Sync; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Tests.Common; +using Umbraco.Cms.Tests.Common.Testing; +using Umbraco.Cms.Tests.Integration.Testing; +using Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services; + +[TestFixture] +[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Mock)] +public abstract class PublishedUrlInfoProviderTestsBase : UmbracoIntegrationTestWithContent +{ + protected IDocumentUrlService DocumentUrlService => GetRequiredService(); + + protected IPublishedUrlInfoProvider PublishedUrlInfoProvider => GetRequiredService(); + + protected override void CustomTestSetup(IUmbracoBuilder builder) + { + builder.Services.AddUnique(); + builder.AddNotificationHandler(); + builder.Services.AddNotificationAsyncHandler(); + builder.Services.AddUnique(serviceProvider => new TestUmbracoContextAccessor(GetUmbracoContext(serviceProvider))); + builder.Services.AddUnique(CreateHttpContextAccessor()); + } + + public override void Setup() + { + DocumentUrlService.InitAsync(false, CancellationToken.None).GetAwaiter().GetResult(); + base.Setup(); + } + + private IUmbracoContext GetUmbracoContext(IServiceProvider serviceProvider) + { + var mock = new Mock(); + + mock.Setup(x => x.Content).Returns(serviceProvider.GetRequiredService()); + mock.Setup(x => x.CleanedUmbracoUrl).Returns(new Uri("https://localhost:44339")); + + return mock.Object; + } + + private IHttpContextAccessor CreateHttpContextAccessor() + { + var mock = new Mock(); + var httpContext = new DefaultHttpContext(); + httpContext.Request.Scheme = "https"; + httpContext.Request.Host = new HostString("localhost"); + + mock.Setup(x => x.HttpContext).Returns(httpContext); + return mock.Object; + } +} diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProvider_hidetoplevel_false.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProvider_hidetoplevel_false.cs new file mode 100644 index 0000000000..b0f1a03ec1 --- /dev/null +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishedUrlInfoProvider_hidetoplevel_false.cs @@ -0,0 +1,45 @@ +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Tests.Common.Builders; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services; + +public class PublishedUrlInfoProvider_hidetoplevel_false : PublishedUrlInfoProviderTestsBase +{ + protected override void CustomTestSetup(IUmbracoBuilder builder) + { + builder.Services.Configure(x => x.HideTopLevelNodeFromPath = false); + base.CustomTestSetup(builder); + } + + [Test] + public async Task Two_items_in_level_1_with_same_name_will_not_have_conflicting_routes() + { + // Create a second root + var secondRoot = ContentBuilder.CreateSimpleContent(ContentType, "Second Root", null); + var contentSchedule = ContentScheduleCollection.CreateWithEntry(DateTime.Now.AddMinutes(-5), null); + ContentService.Save(secondRoot, -1, contentSchedule); + + // Create a child of second root + var childOfSecondRoot = ContentBuilder.CreateSimpleContent(ContentType, Subpage.Name, secondRoot); + childOfSecondRoot.Key = new Guid("FF6654FB-BC68-4A65-8C6C-135567F50BD6"); + ContentService.Save(childOfSecondRoot, -1, contentSchedule); + + // Publish both the main root and the second root with descendants + ContentService.PublishBranch(Textpage, true, new[] { "*" }); + ContentService.PublishBranch(secondRoot, true, new[] { "*" }); + + var subPageUrls = await PublishedUrlInfoProvider.GetAllAsync(Subpage); + var childOfSecondRootUrls = await PublishedUrlInfoProvider.GetAllAsync(childOfSecondRoot); + + Assert.AreEqual(1, subPageUrls.Count); + Assert.IsTrue(subPageUrls.First().IsUrl); + Assert.AreEqual("/textpage/text-page-1/", subPageUrls.First().Text); + + Assert.AreEqual(1, childOfSecondRootUrls.Count); + Assert.IsTrue(childOfSecondRootUrls.First().IsUrl); + Assert.AreEqual("/second-root/text-page-1/", childOfSecondRootUrls.First().Text); + } +} diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index 50d67af360..9efd93a4bc 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -247,5 +247,11 @@ MediaNavigationServiceTests.cs + + PublishedUrlInfoProviderTestsBase.cs + + + PublishedUrlInfoProviderTestsBase.cs +