Skip to content

Commit a9f5e81

Browse files
authored
Merge branch 'umbraco:main' into temp/18872
2 parents fd5c8bc + e6967a4 commit a9f5e81

File tree

7 files changed

+146
-19
lines changed

7 files changed

+146
-19
lines changed

build/nightly-E2E-test-pipelines.yml

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ stages:
129129
# E2E Tests
130130
- job:
131131
displayName: E2E Tests (SQLite)
132-
# currently disabled due to DB locks randomly occuring.
133132
condition: eq(${{parameters.sqliteAcceptanceTests}}, True)
134133
timeoutInMinutes: 180
135134
variables:
@@ -140,22 +139,22 @@ stages:
140139
matrix:
141140
LinuxPart1Of3:
142141
vmImage: "ubuntu-latest"
143-
testCommand: "npm run test -- --shard=1/3"
142+
testCommand: "npm run testSqlite -- --shard=1/3"
144143
LinuxPart2Of3:
145144
vmImage: "ubuntu-latest"
146-
testCommand: "npm run test -- --shard=2/3"
145+
testCommand: "npm run testSqlite -- --shard=2/3"
147146
LinuxPart3Of3:
148147
vmImage: "ubuntu-latest"
149-
testCommand: "npm run test -- --shard=3/3"
148+
testCommand: "npm run testSqlite -- --shard=3/3"
150149
WindowsPart1Of3:
151150
vmImage: "windows-latest"
152-
testCommand: "npm run test -- --shard=1/3"
151+
testCommand: "npm run testSqlite -- --shard=1/3"
153152
WindowsPart2Of3:
154153
vmImage: "windows-latest"
155-
testCommand: "npm run test -- --shard=2/3"
154+
testCommand: "npm run testSqlite -- --shard=2/3"
156155
WindowsPart3Of3:
157156
vmImage: "windows-latest"
158-
testCommand: "npm run test -- --shard=3/3"
157+
testCommand: "npm run testSqlite -- --shard=3/3"
159158
pool:
160159
vmImage: $(vmImage)
161160
steps:
@@ -297,29 +296,29 @@ stages:
297296
strategy:
298297
matrix:
299298
LinuxPart1Of3:
300-
testCommand: "npm run testSqlite -- --shard=1/3"
299+
testCommand: "npm run test -- --shard=1/3"
301300
vmImage: "ubuntu-latest"
302301
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
303302
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
304303
LinuxPart2Of3:
305-
testCommand: "npm run testSqlite -- --shard=2/3"
304+
testCommand: "npm run test -- --shard=2/3"
306305
vmImage: "ubuntu-latest"
307306
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
308307
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
309308
LinuxPart3Of3:
310-
testCommand: "npm run testSqlite -- --shard=3/3"
309+
testCommand: "npm run test -- --shard=3/3"
311310
vmImage: "ubuntu-latest"
312311
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
313312
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
314313
WindowsPart1Of3:
315314
vmImage: "windows-latest"
316-
testCommand: "npm run testSqlite -- --shard=1/3"
315+
testCommand: "npm run test -- --shard=1/3"
317316
WindowsPart2Of3:
318317
vmImage: "windows-latest"
319-
testCommand: "npm run testSqlite -- --shard=2/3"
318+
testCommand: "npm run test -- --shard=2/3"
320319
WindowsPart3Of3:
321320
vmImage: "windows-latest"
322-
testCommand: "npm run testSqlite -- --shard=3/3"
321+
testCommand: "npm run test -- --shard=3/3"
323322
pool:
324323
vmImage: $(vmImage)
325324
steps:

src/Umbraco.Cms.Api.Delivery/Services/RoutingServiceBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ protected Uri GetDefaultRequestUri(string requestedPath)
3636
}
3737

3838
protected static string GetContentRoute(DomainAndUri domainAndUri, Uri contentRoute)
39-
=> $"{domainAndUri.ContentId}{DomainUtilities.PathRelativeToDomain(domainAndUri.Uri, contentRoute.AbsolutePath)}";
39+
=> $"{domainAndUri.ContentId}{DomainUtilities.PathRelativeToDomain(domainAndUri.Uri, contentRoute.LocalPath)}"; // Use LocalPath over AbsolutePath to keep the path decoded.
4040

4141
protected DomainAndUri? GetDomainAndUriForRoute(Uri contentUrl)
4242
{

src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
2323
<_Parameter1>Umbraco.Tests.UnitTests</_Parameter1>
2424
</AssemblyAttribute>
25+
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
26+
<_Parameter1>Umbraco.Tests.Integration</_Parameter1>
27+
</AssemblyAttribute>
2528
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
2629
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
2730
</AssemblyAttribute>

src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextElementParser.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ private T ParseElement<T>(HtmlNode element, IPublishedContentCache contentCache,
9898
// - non-#comment nodes
9999
// - non-#text nodes
100100
// - non-empty #text nodes
101-
// - empty #text between inline elements (see #17037)
101+
// - empty #text between inline elements (see #17037) but not #text with only newlines (see #19388)
102102
HtmlNode[] childNodes = element.ChildNodes
103-
.Where(c => c.Name != CommentNodeName && (c.Name != TextNodeName || c.NextSibling is not null || string.IsNullOrWhiteSpace(c.InnerText) is false))
103+
.Where(c => c.Name != CommentNodeName && (c.Name != TextNodeName || IsNonEmptyElement(c)))
104104
.ToArray();
105105

106106
var tag = TagName(element);
@@ -121,6 +121,9 @@ private T ParseElement<T>(HtmlNode element, IPublishedContentCache contentCache,
121121
return createElement(tag, attributes, childElements);
122122
}
123123

124+
private static bool IsNonEmptyElement(HtmlNode htmlNode) =>
125+
string.IsNullOrWhiteSpace(htmlNode.InnerText) is false || htmlNode.InnerText.Any(c => c != '\n' && c != '\r');
126+
124127
private static string TagName(HtmlNode htmlNode) => htmlNode.Name;
125128

126129
private void ReplaceLocalLinks(IPublishedContentCache contentCache, IPublishedMediaCache mediaCache, Dictionary<string, object> attributes)

tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/ContentWithMultiURLPicker.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ test.afterEach(async ({umbracoApi}) => {
1717
await umbracoApi.documentType.ensureNameNotExists(documentTypeName);
1818
});
1919

20-
test('can create content with the document link', {tag: '@smoke'}, async ({umbracoApi, umbracoUi}) => {
20+
// TODO, this is flaky on the pipeline, not locally. Look into why
21+
test.fixme('can create content with the document link', {tag: '@smoke'}, async ({umbracoApi, umbracoUi}) => {
2122
// Arrange
2223
const expectedState = 'Draft';
2324
const dataTypeData = await umbracoApi.dataType.getByName(dataTypeName);
@@ -87,7 +88,7 @@ test('can publish content with the document link', async ({umbracoApi, umbracoUi
8788
await umbracoUi.content.clickSaveAndPublishButton();
8889

8990
// Assert
90-
//await umbracoUi.content.doesSuccessNotificationsHaveCount(2);
91+
//await umbracoUi.content.doesSuccessNotificationsHaveCount(2);
9192
await umbracoUi.content.isErrorNotificationVisible(false);
9293
expect(await umbracoApi.document.doesNameExist(contentName)).toBeTruthy();
9394
const contentData = await umbracoApi.document.getByName(contentName);
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Moq;
4+
using NUnit.Framework;
5+
using Umbraco.Cms.Api.Delivery.Services;
6+
using Umbraco.Cms.Core.Cache;
7+
using Umbraco.Cms.Core.DeliveryApi;
8+
using Umbraco.Cms.Core.PublishedCache;
9+
using Umbraco.Cms.Core.Routing;
10+
using Umbraco.Cms.Tests.Common.Testing;
11+
using Umbraco.Cms.Tests.Integration.Testing;
12+
13+
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.DeliveryApi;
14+
15+
[TestFixture]
16+
[UmbracoTest(
17+
Database = UmbracoTestOptions.Database.NewSchemaPerFixture,
18+
WithApplication = true)]
19+
public class RequestRoutingServiceTests : UmbracoIntegrationTest
20+
{
21+
private IRequestRoutingService RequestRoutingService => GetRequiredService<IRequestRoutingService>();
22+
23+
protected override void CustomTestSetup(IUmbracoBuilder builder)
24+
{
25+
builder.Services.AddUnique<IRequestRoutingService, RequestRoutingService>();
26+
27+
var elementCache = new FastDictionaryAppCache();
28+
var snapshotCache = new FastDictionaryAppCache();
29+
30+
var domainCacheMock = new Mock<IDomainCache>();
31+
domainCacheMock.Setup(x => x.GetAll(It.IsAny<bool>()))
32+
.Returns(
33+
[
34+
new Domain(1, "localhost/en", 1000, "en-us", false, 0),
35+
new Domain(2, "localhost/jp", 1000, "ja-jp", false, 1),
36+
]);
37+
builder.Services.AddSingleton(provider => domainCacheMock.Object);
38+
}
39+
40+
[TestCase(null, "")]
41+
[TestCase("", "")]
42+
[TestCase("/", "/")]
43+
[TestCase("/en/test/", "1000/test/")] // Verifies matching a domain.
44+
[TestCase("/da/test/", "/da/test/")] // Verifies that with no matching domain, so route will be returned as is.
45+
[TestCase("/jp/オフィス/", "1000/オフィス/")] // Verifies that with a URL segment containing special characters, the route remains decoded.
46+
public void GetContentRoute_ReturnsExpectedRoute(string? requestedRoute, string expectedResult)
47+
{
48+
if (!string.IsNullOrEmpty(requestedRoute))
49+
{
50+
var httpContextAccessor = GetRequiredService<IHttpContextAccessor>();
51+
52+
httpContextAccessor.HttpContext = new DefaultHttpContext
53+
{
54+
Request =
55+
{
56+
Scheme = "https",
57+
Host = new HostString("localhost"),
58+
Path = requestedRoute,
59+
},
60+
};
61+
}
62+
63+
var result = RequestRoutingService.GetContentRoute(requestedRoute);
64+
Assert.AreEqual(expectedResult, result);
65+
}
66+
}

tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/RichTextParserTests.cs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,16 +357,71 @@ public void ParseElement_CanHandleMixedInlineAndBlockLevelBlocks()
357357
Assert.IsEmpty(blockLevelBlock.Elements);
358358
}
359359

360+
private const string TestParagraph = "What follows from <strong>here</strong> <em>is</em> <a href=\"#\">just</a> a bunch of text.";
361+
360362
[Test]
361363
public void ParseElement_CanHandleWhitespaceAroundInlineElemements()
362364
{
363365
var parser = CreateRichTextElementParser();
364366

365-
var element = parser.Parse("<p>What follows from <strong>here</strong> <em>is</em> <a href=\"#\">just</a> a bunch of text.</p>", RichTextBlockModel.Empty) as RichTextRootElement;
367+
var element = parser.Parse($"<p>{TestParagraph}</p>", RichTextBlockModel.Empty) as RichTextRootElement;
366368
Assert.IsNotNull(element);
367369
var paragraphElement = element.Elements.Single() as RichTextGenericElement;
368370
Assert.IsNotNull(paragraphElement);
369371

372+
AssertTestParagraph(paragraphElement);
373+
}
374+
375+
[TestCase(1, "\n")]
376+
[TestCase(2, "\n")]
377+
[TestCase(1, "\r")]
378+
[TestCase(2, "\r")]
379+
[TestCase(1, "\r\n")]
380+
[TestCase(2, "\r\n")]
381+
public void ParseElement_RemovesNewLinesAroundHtmlStructuralElements(int numberOfNewLineCharacters, string newlineCharacter)
382+
{
383+
var parser = CreateRichTextElementParser();
384+
385+
var newLineSeparator = string.Concat(Enumerable.Repeat(newlineCharacter, numberOfNewLineCharacters));
386+
var element = parser.Parse($"<table>{newLineSeparator}<tr>{newLineSeparator}<td>{TestParagraph}</td>{newLineSeparator}</tr>{newLineSeparator}</table>", RichTextBlockModel.Empty) as RichTextRootElement;
387+
Assert.IsNotNull(element);
388+
var tableElement = element.Elements.Single() as RichTextGenericElement;
389+
Assert.IsNotNull(tableElement);
390+
391+
var rowElement = tableElement.Elements.Single() as RichTextGenericElement;
392+
Assert.IsNotNull(rowElement);
393+
394+
var cellElement = rowElement.Elements.Single() as RichTextGenericElement;
395+
Assert.IsNotNull(cellElement);
396+
397+
AssertTestParagraph(cellElement);
398+
}
399+
400+
[TestCase(1, "\n")]
401+
[TestCase(2, "\n")]
402+
[TestCase(1, "\r")]
403+
[TestCase(2, "\r")]
404+
[TestCase(1, "\r\n")]
405+
[TestCase(2, "\r\n")]
406+
public void ParseElement_RemovesNewLinesAroundHtmlContentElements(int numberOfNewLineCharacters, string newlineCharacter)
407+
{
408+
var parser = CreateRichTextElementParser();
409+
410+
var newLineSeparator = string.Concat(Enumerable.Repeat(newlineCharacter, numberOfNewLineCharacters));
411+
var element = parser.Parse($"<div><p>{TestParagraph}</p>{newLineSeparator}<p></p>{newLineSeparator}<p>&nbsp;</p>{newLineSeparator}<p>{TestParagraph}</p></div>", RichTextBlockModel.Empty) as RichTextRootElement;
412+
Assert.IsNotNull(element);
413+
var divElement = element.Elements.Single() as RichTextGenericElement;
414+
Assert.IsNotNull(divElement);
415+
416+
var paragraphELements = divElement.Elements;
417+
Assert.AreEqual(4, paragraphELements.Count());
418+
419+
AssertTestParagraph(paragraphELements.First() as RichTextGenericElement);
420+
AssertTestParagraph(paragraphELements.Last() as RichTextGenericElement);
421+
}
422+
423+
private static void AssertTestParagraph(RichTextGenericElement paragraphElement)
424+
{
370425
var childElements = paragraphElement.Elements.ToArray();
371426
Assert.AreEqual(7, childElements.Length);
372427

0 commit comments

Comments
 (0)