Merge branch 'release/17.0' into v17/dev
# Conflicts: # src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-action/restore-from-recycle-bin/restore-from-recycle-bin.action.ts # tests/Umbraco.Tests.AcceptanceTest/package-lock.json # tests/Umbraco.Tests.AcceptanceTest/package.json
This commit is contained in:
16
tests/Umbraco.Tests.AcceptanceTest/package-lock.json
generated
16
tests/Umbraco.Tests.AcceptanceTest/package-lock.json
generated
@@ -8,7 +8,7 @@
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@umbraco/json-models-builders": "^2.0.40",
|
||||
"@umbraco/playwright-testhelpers": "^17.0.0-beta.4",
|
||||
"@umbraco/playwright-testhelpers": "^17.0.0-beta.7",
|
||||
"camelize": "^1.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"node-fetch": "^2.6.7"
|
||||
@@ -58,21 +58,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@umbraco/json-models-builders": {
|
||||
"version": "2.0.40",
|
||||
"resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-2.0.40.tgz",
|
||||
"integrity": "sha512-Yqojp/0akRgXsnjg18+MjMdkRvFrmlUNbfITgZ3d1h/PIRbWXPNKY1YAfZmdUv+g1SRSHrbIRpPPtSy+gNOjHw==",
|
||||
"version": "2.0.41",
|
||||
"resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-2.0.41.tgz",
|
||||
"integrity": "sha512-rCNUHCOpcuWIj7xUhk0lpcn4jzk9y82jHs9FSb7kxH716AnDyYvwuI+J0Ayd4hhWtXXqNCRqugCNYjG+rvzshQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"camelize": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@umbraco/playwright-testhelpers": {
|
||||
"version": "17.0.0-beta.4",
|
||||
"resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-17.0.0-beta.4.tgz",
|
||||
"integrity": "sha512-+OE1A2oAdFel4myf5T/jJLuw0aLvSOUBplkUfsYFj2ACeLygfAp/MM7q2RQ+YlCym/wdF+jAqJM3g+zsKEDjaQ==",
|
||||
"version": "17.0.0-beta.7",
|
||||
"resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-17.0.0-beta.7.tgz",
|
||||
"integrity": "sha512-5fhmVVSpJkH6Inx8nA9qqqvZzYuPdDxJdQF2IzY0oSf8C0eti+TJ2BKrYfTLmZTfVqmHUas72BMGser5pfpl9A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@umbraco/json-models-builders": "2.0.40",
|
||||
"@umbraco/json-models-builders": "2.0.41",
|
||||
"node-fetch": "^2.6.7"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@umbraco/json-models-builders": "^2.0.40",
|
||||
"@umbraco/playwright-testhelpers": "^17.0.0-beta.4",
|
||||
"@umbraco/playwright-testhelpers": "^17.0.0-beta.7",
|
||||
"camelize": "^1.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"node-fetch": "^2.6.7"
|
||||
|
||||
@@ -51,7 +51,7 @@ test('cannot create child content if allowed child node is disabled', async ({um
|
||||
|
||||
// Assert
|
||||
await umbracoUi.content.isDocumentTypeNameVisible(documentTypeName, false);
|
||||
await umbracoUi.content.doesModalHaveText(noAllowedDocumentTypeAvailableMessage);
|
||||
await umbracoUi.content.doesDocumentModalHaveText(noAllowedDocumentTypeAvailableMessage);
|
||||
});
|
||||
|
||||
test('can create multiple child nodes with different document types', async ({umbracoApi, umbracoUi}) => {
|
||||
|
||||
@@ -51,7 +51,7 @@ test('can create content using an invariant document blueprint', async ({umbraco
|
||||
await umbracoUi.content.clickActionsMenuAtRoot();
|
||||
await umbracoUi.content.clickCreateActionMenuOption();
|
||||
await umbracoUi.content.chooseDocumentType(documentTypeName);
|
||||
await umbracoUi.content.clickModalMenuItemWithName(documentBlueprintName);
|
||||
await umbracoUi.content.selectDocumentBlueprintWithName(documentBlueprintName);
|
||||
await umbracoUi.content.clickSaveButtonForContent();
|
||||
|
||||
// Assert
|
||||
@@ -75,7 +75,7 @@ test('can create content using a variant document blueprint', async ({umbracoApi
|
||||
await umbracoUi.content.clickActionsMenuAtRoot();
|
||||
await umbracoUi.content.clickCreateActionMenuOption();
|
||||
await umbracoUi.content.chooseDocumentType(documentTypeName);
|
||||
await umbracoUi.content.clickModalMenuItemWithName(documentBlueprintName);
|
||||
await umbracoUi.content.selectDocumentBlueprintWithName(documentBlueprintName);
|
||||
await umbracoUi.content.clickSaveButtonForContent();
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
|
||||
@@ -104,7 +104,7 @@ test('can create content with different name using an invariant document bluepri
|
||||
await umbracoUi.content.clickActionsMenuAtRoot();
|
||||
await umbracoUi.content.clickCreateActionMenuOption();
|
||||
await umbracoUi.content.chooseDocumentType(documentTypeName);
|
||||
await umbracoUi.content.clickModalMenuItemWithName(documentBlueprintName);
|
||||
await umbracoUi.content.selectDocumentBlueprintWithName(documentBlueprintName);
|
||||
await umbracoUi.content.enterContentName(contentName);
|
||||
await umbracoUi.content.clickSaveButtonForContent();
|
||||
|
||||
@@ -130,7 +130,7 @@ test('can create content with different name using a variant document blueprint'
|
||||
await umbracoUi.content.clickActionsMenuAtRoot();
|
||||
await umbracoUi.content.clickCreateActionMenuOption();
|
||||
await umbracoUi.content.chooseDocumentType(documentTypeName);
|
||||
await umbracoUi.content.clickModalMenuItemWithName(documentBlueprintName);
|
||||
await umbracoUi.content.selectDocumentBlueprintWithName(documentBlueprintName);
|
||||
await umbracoUi.content.enterContentName(contentName);
|
||||
await umbracoUi.content.clickSaveButtonForContent();
|
||||
await umbracoUi.content.clickSaveButton();
|
||||
@@ -161,7 +161,7 @@ test('can create content using a document blueprint with block list', async ({um
|
||||
await umbracoUi.content.clickActionsMenuAtRoot();
|
||||
await umbracoUi.content.clickCreateActionMenuOption();
|
||||
await umbracoUi.content.chooseDocumentType(documentTypeName);
|
||||
await umbracoUi.content.clickModalMenuItemWithName(documentBlueprintName);
|
||||
await umbracoUi.content.selectDocumentBlueprintWithName(documentBlueprintName);
|
||||
await umbracoUi.content.clickSaveButtonForContent();
|
||||
|
||||
// Assert
|
||||
@@ -187,7 +187,7 @@ test('can create content using a document blueprint with block grid', async ({um
|
||||
await umbracoUi.content.clickActionsMenuAtRoot();
|
||||
await umbracoUi.content.clickCreateActionMenuOption();
|
||||
await umbracoUi.content.chooseDocumentType(documentTypeName);
|
||||
await umbracoUi.content.clickModalMenuItemWithName(documentBlueprintName);
|
||||
await umbracoUi.content.selectDocumentBlueprintWithName(documentBlueprintName);
|
||||
await umbracoUi.content.clickSaveButtonForContent();
|
||||
|
||||
// Assert
|
||||
@@ -197,4 +197,4 @@ test('can create content using a document blueprint with block grid', async ({um
|
||||
expect(contentData.values[0].value.contentData[0].values[0].value.markup).toEqual(textContent);
|
||||
const blockListValue = contentData.values.find(item => item.editorAlias === "Umbraco.BlockGrid")?.value;
|
||||
expect(blockListValue).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,7 +23,9 @@ test('can create a document blueprint from the settings menu', {tag: '@smoke'},
|
||||
// Act
|
||||
await umbracoUi.documentBlueprint.clickActionsMenuAtRoot();
|
||||
await umbracoUi.documentBlueprint.clickCreateActionMenuOption();
|
||||
await umbracoUi.documentBlueprint.clickCreateNewDocumentBlueprintButton();
|
||||
await umbracoUi.documentBlueprint.clickTextButtonWithName(documentTypeName);
|
||||
await umbracoUi.documentBlueprint.clickChooseButton();
|
||||
await umbracoUi.documentBlueprint.enterDocumentBlueprintName(documentBlueprintName);
|
||||
await umbracoUi.documentBlueprint.clickSaveButton();
|
||||
|
||||
@@ -108,7 +110,9 @@ test('can create a variant document blueprint', {tag: '@release'}, async ({umbra
|
||||
// Act
|
||||
await umbracoUi.documentBlueprint.clickActionsMenuAtRoot();
|
||||
await umbracoUi.documentBlueprint.clickCreateActionMenuOption();
|
||||
await umbracoUi.documentBlueprint.clickCreateNewDocumentBlueprintButton();
|
||||
await umbracoUi.documentBlueprint.clickTextButtonWithName(documentTypeName);
|
||||
await umbracoUi.documentBlueprint.clickChooseButton();
|
||||
await umbracoUi.documentBlueprint.enterDocumentBlueprintName(documentBlueprintName);
|
||||
await umbracoUi.documentBlueprint.clickSaveButton();
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ test('can remove a header from a webhook', async ({umbracoApi, umbracoUi}) => {
|
||||
expect(await umbracoApi.webhook.doesWebhookHaveHeader(webhookName, headerName, headerValue)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('cannot add both content event and media event for a webhook', {tag: '@release'}, async ({umbracoApi, umbracoUi}) => {
|
||||
test('cannot add both content event and media event for a webhook', async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const event = 'Content Published';
|
||||
await umbracoApi.webhook.createDefaultWebhook(webhookName, webhookSiteToken, event);
|
||||
@@ -185,4 +185,4 @@ test('cannot add both content event and media event for a webhook', {tag: '@rele
|
||||
// Assert
|
||||
await umbracoUi.webhook.isModalMenuItemWithNameDisabled('Media Saved');
|
||||
await umbracoUi.webhook.isModalMenuItemWithNameDisabled('Media Deleted');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ test.afterEach(async ({umbracoApi}) => {
|
||||
await umbracoApi.media.ensureNameNotExists(mediaName);
|
||||
});
|
||||
|
||||
test('can trigger when content is published', {tag: '@release'}, async ({umbracoApi, umbracoUi}) => {
|
||||
test('can trigger when content is published', async ({umbracoApi, umbracoUi}) => {
|
||||
test.slow();
|
||||
|
||||
// Arrange
|
||||
|
||||
@@ -34,9 +34,7 @@ public abstract class UmbracoIntegrationTestWithContentEditing : UmbracoIntegrat
|
||||
|
||||
protected ContentCreateModel Textpage { get; private set; }
|
||||
|
||||
protected ContentScheduleCollection ContentSchedule { get; private set; }
|
||||
|
||||
protected CultureAndScheduleModel CultureAndSchedule { get; private set; }
|
||||
protected ICollection<CulturePublishScheduleModel> CultureAndSchedule { get; private set; }
|
||||
|
||||
protected int TextpageId { get; private set; }
|
||||
|
||||
@@ -91,11 +89,7 @@ public abstract class UmbracoIntegrationTestWithContentEditing : UmbracoIntegrat
|
||||
}
|
||||
|
||||
// Sets the culture and schedule for the content, in this case, we are publishing immediately for all cultures
|
||||
ContentSchedule = new ContentScheduleCollection();
|
||||
CultureAndSchedule = new CultureAndScheduleModel
|
||||
{
|
||||
CulturesToPublishImmediately = new HashSet<string> { "*" }, Schedules = ContentSchedule,
|
||||
};
|
||||
CultureAndSchedule = [new CulturePublishScheduleModel { Culture = "*", Schedule = null }];
|
||||
|
||||
// Create and Save Content "Text Page 1" based on "umbTextpage" -> 1054
|
||||
PublishedTextPage = ContentEditingBuilder.CreateSimpleContent(ContentType.Key, "Published Page");
|
||||
|
||||
@@ -35,6 +35,8 @@ public class PublishedContentFallbackTests : UmbracoIntegrationTest
|
||||
|
||||
private IApiContentBuilder ApiContentBuilder => GetRequiredService<IApiContentBuilder>();
|
||||
|
||||
private ILanguageService LanguageService => GetRequiredService<ILanguageService>();
|
||||
|
||||
protected override void CustomTestSetup(IUmbracoBuilder builder)
|
||||
=> builder
|
||||
.AddUmbracoHybridCache()
|
||||
@@ -98,6 +100,36 @@ public class PublishedContentFallbackTests : UmbracoIntegrationTest
|
||||
Assert.AreEqual(invariantTitle, invariantValue);
|
||||
}
|
||||
|
||||
[TestCase("Danish title", true)]
|
||||
[TestCase("Danish title", false)]
|
||||
[TestCase(null, true)]
|
||||
[TestCase(null, false)]
|
||||
public async Task Property_Value_Can_Perform_Explicit_Language_Fallback(string? danishTitle, bool performFallbackToDefaultLanguage)
|
||||
{
|
||||
var danishLanguage = new Language("da-DK", "Danish")
|
||||
{
|
||||
FallbackIsoCode = "en-US"
|
||||
};
|
||||
await LanguageService.CreateAsync(danishLanguage, Constants.Security.SuperUserKey);
|
||||
|
||||
UmbracoContextFactory.EnsureUmbracoContext();
|
||||
|
||||
const string englishTitle = "English title";
|
||||
var publishedContent = await SetupCultureVariantContentAsync(englishTitle, danishTitle);
|
||||
|
||||
VariationContextAccessor.VariationContext = new VariationContext(culture: "da-DK", segment: null);
|
||||
var danishValue = publishedContent.Value<string>(PublishedValueFallback, "title");
|
||||
Assert.AreEqual(danishTitle ?? string.Empty, danishValue);
|
||||
|
||||
var fallback = performFallbackToDefaultLanguage ? Fallback.ToDefaultLanguage : Fallback.ToLanguage;
|
||||
var fallbackValue = publishedContent.Value<string>(PublishedValueFallback, "title", fallback: fallback);
|
||||
Assert.AreEqual(danishTitle ?? englishTitle, fallbackValue);
|
||||
|
||||
VariationContextAccessor.VariationContext = new VariationContext(culture: "en-US", segment: null);
|
||||
var englishValue = publishedContent.Value<string>(PublishedValueFallback, "title");
|
||||
Assert.AreEqual(englishTitle, englishValue);
|
||||
}
|
||||
|
||||
private async Task<IPublishedContent> SetupSegmentedContentAsync(string? invariantTitle, string? segmentedTitle)
|
||||
{
|
||||
var contentType = new ContentTypeBuilder()
|
||||
@@ -124,11 +156,47 @@ public class PublishedContentFallbackTests : UmbracoIntegrationTest
|
||||
ContentService.Save(content);
|
||||
ContentService.Publish(content, ["*"]);
|
||||
|
||||
return GetPublishedContent(content.Key);
|
||||
}
|
||||
|
||||
private async Task<IPublishedContent> SetupCultureVariantContentAsync(string englishTitle, string? danishTitle)
|
||||
{
|
||||
var contentType = new ContentTypeBuilder()
|
||||
.WithAlias("theContentType")
|
||||
.WithContentVariation(ContentVariation.Culture)
|
||||
.AddPropertyType()
|
||||
.WithAlias("title")
|
||||
.WithName("Title")
|
||||
.WithDataTypeId(Constants.DataTypes.Textbox)
|
||||
.WithPropertyEditorAlias(Constants.PropertyEditors.Aliases.TextBox)
|
||||
.WithValueStorageType(ValueStorageType.Nvarchar)
|
||||
.WithVariations(ContentVariation.Culture)
|
||||
.Done()
|
||||
.WithAllowAsRoot(true)
|
||||
.Build();
|
||||
await ContentTypeService.CreateAsync(contentType, Constants.Security.SuperUserKey);
|
||||
|
||||
var content = new ContentBuilder()
|
||||
.WithContentType(contentType)
|
||||
.WithCultureName("en-US", "EN")
|
||||
.WithCultureName("da-DK", "DA")
|
||||
.WithName("Content")
|
||||
.Build();
|
||||
content.SetValue("title", englishTitle, culture: "en-US");
|
||||
content.SetValue("title", danishTitle, culture: "da-DK");
|
||||
ContentService.Save(content);
|
||||
ContentService.Publish(content, ["en-US", "da-DK"]);
|
||||
|
||||
return GetPublishedContent(content.Key);
|
||||
}
|
||||
|
||||
private IPublishedContent GetPublishedContent(Guid key)
|
||||
{
|
||||
ContentCacheRefresher.Refresh([new ContentCacheRefresher.JsonPayload { ChangeTypes = TreeChangeTypes.RefreshAll }]);
|
||||
|
||||
UmbracoContextAccessor.Clear();
|
||||
var umbracoContext = UmbracoContextFactory.EnsureUmbracoContext().UmbracoContext;
|
||||
var publishedContent = umbracoContext.Content.GetById(content.Key);
|
||||
var publishedContent = umbracoContext.Content.GetById(key);
|
||||
Assert.IsNotNull(publishedContent);
|
||||
|
||||
return publishedContent;
|
||||
|
||||
@@ -1932,4 +1932,95 @@ internal partial class BlockListElementLevelVariationTests
|
||||
Assert.AreEqual(expectedPickedContent.Key, actualPickedPublishedContent.Key);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(ContentVariation.Culture, false)]
|
||||
[TestCase(ContentVariation.Culture, true)]
|
||||
[TestCase(ContentVariation.Nothing, false)]
|
||||
[TestCase(ContentVariation.Nothing, true)]
|
||||
public async Task Can_Perform_Language_Fallback(ContentVariation elementTypeVariation, bool performFallbackToDefaultLanguage)
|
||||
{
|
||||
var daDkLanguage = await LanguageService.GetAsync("da-DK");
|
||||
Assert.IsNotNull(daDkLanguage);
|
||||
daDkLanguage.FallbackIsoCode = "en-US";
|
||||
var saveLanguageResult = await LanguageService.UpdateAsync(daDkLanguage, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(saveLanguageResult.Success);
|
||||
|
||||
daDkLanguage = await LanguageService.GetAsync("da-DK");
|
||||
Assert.AreEqual("en-US", daDkLanguage?.FallbackIsoCode);
|
||||
|
||||
var elementType = CreateElementType(elementTypeVariation);
|
||||
var blockListDataType = await CreateBlockListDataType(elementType);
|
||||
var contentType = CreateContentType(ContentVariation.Culture, blockListDataType, ContentVariation.Culture);
|
||||
|
||||
var content = CreateContent(
|
||||
contentType,
|
||||
elementType,
|
||||
new []
|
||||
{
|
||||
new BlockProperty(
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "English invariantText content value" },
|
||||
new() { Alias = "variantText", Value = "English variantText content value" }
|
||||
},
|
||||
new List<BlockPropertyValue>
|
||||
{
|
||||
new() { Alias = "invariantText", Value = "English invariantText settings value" },
|
||||
new() { Alias = "variantText", Value = "English variantText settings value" }
|
||||
},
|
||||
"en-US",
|
||||
null)
|
||||
},
|
||||
true);
|
||||
|
||||
AssertPropertyValuesWithFallback("en-US",
|
||||
"English invariantText content value", "English variantText content value",
|
||||
"English invariantText settings value", "English variantText settings value");
|
||||
|
||||
AssetEmptyPropertyValues("da-DK");
|
||||
|
||||
AssertPropertyValuesWithFallback("da-DK",
|
||||
"English invariantText content value", "English variantText content value",
|
||||
"English invariantText settings value", "English variantText settings value");
|
||||
|
||||
void AssertPropertyValuesWithFallback(string culture,
|
||||
string expectedInvariantContentValue, string expectedVariantContentValue,
|
||||
string expectedInvariantSettingsValue, string expectedVariantSettingsValue)
|
||||
{
|
||||
SetVariationContext(culture, null);
|
||||
var publishedContent = GetPublishedContent(content.Key);
|
||||
|
||||
var fallback = performFallbackToDefaultLanguage ? Fallback.ToDefaultLanguage : Fallback.ToLanguage;
|
||||
|
||||
var publishedValueFallback = GetRequiredService<IPublishedValueFallback>();
|
||||
var value = publishedContent.Value<BlockListModel>(publishedValueFallback, "blocks", fallback: fallback);
|
||||
Assert.IsNotNull(value);
|
||||
Assert.AreEqual(1, value.Count);
|
||||
|
||||
var blockListItem = value.First();
|
||||
Assert.AreEqual(2, blockListItem.Content.Properties.Count());
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(expectedInvariantContentValue, blockListItem.Content.Value<string>("invariantText"));
|
||||
Assert.AreEqual(expectedVariantContentValue, blockListItem.Content.Value<string>("variantText"));
|
||||
});
|
||||
|
||||
Assert.AreEqual(2, blockListItem.Settings.Properties.Count());
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(expectedInvariantSettingsValue, blockListItem.Settings.Value<string>("invariantText"));
|
||||
Assert.AreEqual(expectedVariantSettingsValue, blockListItem.Settings.Value<string>("variantText"));
|
||||
});
|
||||
}
|
||||
|
||||
void AssetEmptyPropertyValues(string culture)
|
||||
{
|
||||
SetVariationContext(culture, null);
|
||||
var publishedContent = GetPublishedContent(content.Key);
|
||||
|
||||
var value = publishedContent.Value<BlockListModel>("blocks");
|
||||
Assert.NotNull(value);
|
||||
Assert.IsEmpty(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Cms.Infrastructure.HybridCache;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
using Umbraco.Cms.Tests.Common.Testing;
|
||||
@@ -39,10 +38,10 @@ public class DateTimePropertyEditorTests : UmbracoIntegrationTest
|
||||
|
||||
private static readonly object[] _sourceList1 =
|
||||
[
|
||||
new object[] { Constants.PropertyEditors.Aliases.DateOnly, false, new DateOnly(2025, 1, 22) },
|
||||
new object[] { Constants.PropertyEditors.Aliases.DateOnly, false, new DateOnly(2025, 6, 22) },
|
||||
new object[] { Constants.PropertyEditors.Aliases.TimeOnly, false, new TimeOnly(18, 33, 1) },
|
||||
new object[] { Constants.PropertyEditors.Aliases.DateTimeUnspecified, false, new DateTime(2025, 1, 22, 18, 33, 1) },
|
||||
new object[] { Constants.PropertyEditors.Aliases.DateTimeWithTimeZone, true, new DateTimeOffset(2025, 1, 22, 18, 33, 1, TimeSpan.Zero) },
|
||||
new object[] { Constants.PropertyEditors.Aliases.DateTimeUnspecified, false, new DateTime(2025, 6, 22, 18, 33, 1) },
|
||||
new object[] { Constants.PropertyEditors.Aliases.DateTimeWithTimeZone, true, new DateTimeOffset(2025, 6, 22, 18, 33, 1, TimeSpan.FromHours(2)) },
|
||||
];
|
||||
|
||||
[TestCaseSource(nameof(_sourceList1))]
|
||||
@@ -106,7 +105,7 @@ public class DateTimePropertyEditorTests : UmbracoIntegrationTest
|
||||
.WithValue(
|
||||
new JsonObject
|
||||
{
|
||||
["date"] = "2025-01-22T18:33:01.0000000+00:00",
|
||||
["date"] = "2025-06-22T18:33:01.0000000+02:00",
|
||||
["timeZone"] = "Europe/Copenhagen",
|
||||
})
|
||||
.Done()
|
||||
@@ -127,7 +126,6 @@ public class DateTimePropertyEditorTests : UmbracoIntegrationTest
|
||||
|
||||
Assert.IsTrue(publishResult.Success);
|
||||
|
||||
var test = ((DocumentCache)PublishedContentCache).GetAtRoot(false);
|
||||
var publishedContent = await PublishedContentCache.GetByIdAsync(createContentResult.Result.Content.Key, false);
|
||||
Assert.IsNotNull(publishedContent);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Testing;
|
||||
using Umbraco.Cms.Tests.Integration.Testing;
|
||||
using Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
|
||||
@@ -26,10 +27,10 @@ internal sealed class DocumentHybridCacheTests : UmbracoIntegrationTestWithConte
|
||||
|
||||
private IPublishedContentCache PublishedContentHybridCache => GetRequiredService<IPublishedContentCache>();
|
||||
|
||||
private IContentEditingService ContentEditingService => GetRequiredService<IContentEditingService>();
|
||||
|
||||
private IContentPublishingService ContentPublishingService => GetRequiredService<IContentPublishingService>();
|
||||
|
||||
private IDocumentCacheService DocumentCacheService => GetRequiredService<IDocumentCacheService>();
|
||||
|
||||
private const string NewName = "New Name";
|
||||
private const string NewTitle = "New Title";
|
||||
|
||||
@@ -467,6 +468,61 @@ internal sealed class DocumentHybridCacheTests : UmbracoIntegrationTestWithConte
|
||||
Assert.IsNull(textPage);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Get_Published_Content_By_Id_After_Previous_Check_Where_Not_Found()
|
||||
{
|
||||
// Arrange
|
||||
var testPageKey = Guid.NewGuid();
|
||||
|
||||
// Act & Assert
|
||||
// - assert we cannot get the content that doesn't yet exist from the cache
|
||||
var testPage = await PublishedContentHybridCache.GetByIdAsync(testPageKey);
|
||||
Assert.IsNull(testPage);
|
||||
|
||||
testPage = await PublishedContentHybridCache.GetByIdAsync(testPageKey);
|
||||
Assert.IsNull(testPage);
|
||||
|
||||
// - create and publish the content
|
||||
var testPageContent = ContentEditingBuilder.CreateBasicContent(ContentType.Key, testPageKey);
|
||||
var createResult = await ContentEditingService.CreateAsync(testPageContent, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(createResult.Success);
|
||||
var publishResult = await ContentPublishingService.PublishAsync(testPageKey, CultureAndSchedule, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(publishResult.Success);
|
||||
|
||||
// - assert we can now get the content from the cache
|
||||
testPage = await PublishedContentHybridCache.GetByIdAsync(testPageKey);
|
||||
Assert.IsNotNull(testPage);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Get_Published_Content_By_Id_After_Previous_Exists_Check()
|
||||
{
|
||||
// Act
|
||||
var hasContentForTextPageCached = await DocumentCacheService.HasContentByIdAsync(PublishedTextPageId);
|
||||
Assert.IsTrue(hasContentForTextPageCached);
|
||||
var textPage = await PublishedContentHybridCache.GetByIdAsync(PublishedTextPageId);
|
||||
|
||||
// Assert
|
||||
AssertPublishedTextPage(textPage);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Do_Exists_Check_On_Created_Published_Content()
|
||||
{
|
||||
var testPageKey = Guid.NewGuid();
|
||||
var testPageContent = ContentEditingBuilder.CreateBasicContent(ContentType.Key, testPageKey);
|
||||
var createResult = await ContentEditingService.CreateAsync(testPageContent, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(createResult.Success);
|
||||
var publishResult = await ContentPublishingService.PublishAsync(testPageKey, CultureAndSchedule, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(publishResult.Success);
|
||||
|
||||
var testPage = await PublishedContentHybridCache.GetByIdAsync(testPageKey);
|
||||
Assert.IsNotNull(testPage);
|
||||
|
||||
var hasContentForTextPageCached = await DocumentCacheService.HasContentByIdAsync(testPage.Id);
|
||||
Assert.IsTrue(hasContentForTextPageCached);
|
||||
}
|
||||
|
||||
private void AssertTextPage(IPublishedContent textPage)
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
@@ -31,6 +27,17 @@ public class ObjectExtensionsTests
|
||||
|
||||
private CultureInfo _savedCulture;
|
||||
|
||||
[Test]
|
||||
public void Can_Create_Enumerable_Of_One()
|
||||
{
|
||||
var input = "hello";
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var result = input.AsEnumerableOfOne<string>();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
Assert.AreEqual(1, result.Count());
|
||||
Assert.AreEqual("hello", result.First());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Convert_List_To_Enumerable()
|
||||
{
|
||||
|
||||
@@ -86,7 +86,7 @@ public class DateTimeUnspecifiedValueConverterTests
|
||||
private static object[] _dateTimeUnspecifiedConvertToObjectCases =
|
||||
[
|
||||
new object[] { null, null },
|
||||
new object[] { _convertToObjectInputDate, DateTime.Parse("2025-08-20T17:30:00") },
|
||||
new object[] { _convertToObjectInputDate, DateTime.Parse("2025-08-20T16:30:00") },
|
||||
];
|
||||
|
||||
[TestCaseSource(nameof(_dateTimeUnspecifiedConvertToObjectCases))]
|
||||
|
||||
@@ -86,7 +86,7 @@ public class TimeOnlyValueConverterTests
|
||||
private static object[] _timeOnlyConvertToObjectCases =
|
||||
[
|
||||
new object[] { null, null },
|
||||
new object[] { _convertToObjectInputDate, TimeOnly.Parse("17:30") },
|
||||
new object[] { _convertToObjectInputDate, TimeOnly.Parse("16:30") },
|
||||
];
|
||||
|
||||
[TestCaseSource(nameof(_timeOnlyConvertToObjectCases))]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Caching.Hybrid;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -33,15 +34,15 @@ public class HybridCacheExtensionsTests
|
||||
_cacheMock
|
||||
.Setup(cache => cache.GetOrCreateAsync(
|
||||
key,
|
||||
null!,
|
||||
It.IsAny<Func<object, CancellationToken, ValueTask<ContentCacheNode>>>(),
|
||||
It.IsAny<Func<CancellationToken, ValueTask<ContentCacheNode?>>>(),
|
||||
It.IsAny<Func<Func<CancellationToken, ValueTask<ContentCacheNode?>>, CancellationToken, ValueTask<ContentCacheNode?>>>(),
|
||||
It.IsAny<HybridCacheEntryOptions>(),
|
||||
null,
|
||||
CancellationToken.None))
|
||||
.ReturnsAsync(expectedValue);
|
||||
|
||||
// Act
|
||||
var exists = await HybridCacheExtensions.ExistsAsync<ContentCacheNode>(_cacheMock.Object, key);
|
||||
var exists = await HybridCacheExtensions.ExistsAsync<ContentCacheNode?>(_cacheMock.Object, key, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(exists);
|
||||
@@ -56,24 +57,24 @@ public class HybridCacheExtensionsTests
|
||||
_cacheMock
|
||||
.Setup(cache => cache.GetOrCreateAsync(
|
||||
key,
|
||||
null!,
|
||||
It.IsAny<Func<object, CancellationToken, ValueTask<ContentCacheNode>>>(),
|
||||
It.IsAny<Func<CancellationToken, ValueTask<ContentCacheNode?>>>(),
|
||||
It.IsAny<Func<Func<CancellationToken, ValueTask<ContentCacheNode?>>, CancellationToken, ValueTask<ContentCacheNode?>>>(),
|
||||
It.IsAny<HybridCacheEntryOptions>(),
|
||||
null,
|
||||
CancellationToken.None))
|
||||
.Returns((
|
||||
string key,
|
||||
object? state,
|
||||
Func<object, CancellationToken, ValueTask<ContentCacheNode>> factory,
|
||||
Func<CancellationToken, ValueTask<ContentCacheNode?>> state,
|
||||
Func<Func<CancellationToken, ValueTask<ContentCacheNode?>>, CancellationToken, ValueTask<ContentCacheNode?>> factory,
|
||||
HybridCacheEntryOptions? options,
|
||||
IEnumerable<string>? tags,
|
||||
CancellationToken token) =>
|
||||
{
|
||||
return factory(state!, token);
|
||||
return factory(state, token);
|
||||
});
|
||||
|
||||
// Act
|
||||
var exists = await HybridCacheExtensions.ExistsAsync<ContentCacheNode>(_cacheMock.Object, key);
|
||||
var exists = await HybridCacheExtensions.ExistsAsync<ContentCacheNode?>(_cacheMock.Object, key, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(exists);
|
||||
@@ -89,15 +90,15 @@ public class HybridCacheExtensionsTests
|
||||
_cacheMock
|
||||
.Setup(cache => cache.GetOrCreateAsync(
|
||||
key,
|
||||
null!,
|
||||
It.IsAny<Func<object, CancellationToken, ValueTask<string>>>(),
|
||||
It.IsAny<Func<CancellationToken, ValueTask<string>>>(),
|
||||
It.IsAny<Func<Func<CancellationToken, ValueTask<string>>, CancellationToken, ValueTask<string>>>(),
|
||||
It.IsAny<HybridCacheEntryOptions>(),
|
||||
null,
|
||||
CancellationToken.None))
|
||||
.ReturnsAsync(expectedValue);
|
||||
|
||||
// Act
|
||||
var (exists, value) = await HybridCacheExtensions.TryGetValueAsync<string>(_cacheMock.Object, key);
|
||||
var (exists, value) = await HybridCacheExtensions.TryGetValueAsync<string>(_cacheMock.Object, key, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(exists);
|
||||
@@ -114,15 +115,15 @@ public class HybridCacheExtensionsTests
|
||||
_cacheMock
|
||||
.Setup(cache => cache.GetOrCreateAsync(
|
||||
key,
|
||||
null!,
|
||||
It.IsAny<Func<object, CancellationToken, ValueTask<int>>>(),
|
||||
It.IsAny<Func<CancellationToken, ValueTask<int>>>(),
|
||||
It.IsAny<Func<Func<CancellationToken, ValueTask<int>>, CancellationToken, ValueTask<int>>>(),
|
||||
It.IsAny<HybridCacheEntryOptions>(),
|
||||
null,
|
||||
CancellationToken.None))
|
||||
.ReturnsAsync(expectedValue);
|
||||
|
||||
// Act
|
||||
var (exists, value) = await HybridCacheExtensions.TryGetValueAsync<int>(_cacheMock.Object, key);
|
||||
var (exists, value) = await HybridCacheExtensions.TryGetValueAsync<int>(_cacheMock.Object, key, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(exists);
|
||||
@@ -138,15 +139,15 @@ public class HybridCacheExtensionsTests
|
||||
_cacheMock
|
||||
.Setup(cache => cache.GetOrCreateAsync(
|
||||
key,
|
||||
null!,
|
||||
It.IsAny<Func<object, CancellationToken, ValueTask<object>>>(),
|
||||
It.IsAny<Func<CancellationToken, ValueTask<object>>>(),
|
||||
It.IsAny<Func<Func<CancellationToken, ValueTask<object>>, CancellationToken, ValueTask<object>>>(),
|
||||
It.IsAny<HybridCacheEntryOptions>(),
|
||||
null,
|
||||
CancellationToken.None))
|
||||
.ReturnsAsync(null!);
|
||||
|
||||
// Act
|
||||
var (exists, value) = await HybridCacheExtensions.TryGetValueAsync<int?>(_cacheMock.Object, key);
|
||||
var (exists, value) = await HybridCacheExtensions.TryGetValueAsync<int?>(_cacheMock.Object, key, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(exists);
|
||||
@@ -160,16 +161,16 @@ public class HybridCacheExtensionsTests
|
||||
string key = "test-key";
|
||||
|
||||
_cacheMock.Setup(cache => cache.GetOrCreateAsync(
|
||||
key,
|
||||
null,
|
||||
It.IsAny<Func<object?, CancellationToken, ValueTask<string>>>(),
|
||||
It.IsAny<HybridCacheEntryOptions>(),
|
||||
null,
|
||||
CancellationToken.None))
|
||||
key,
|
||||
It.IsAny<Func<CancellationToken, ValueTask<object>>>(),
|
||||
It.IsAny<Func<Func<CancellationToken, ValueTask<object>>, CancellationToken, ValueTask<object>>>(),
|
||||
It.IsAny<HybridCacheEntryOptions>(),
|
||||
null,
|
||||
CancellationToken.None))
|
||||
.Returns((
|
||||
string key,
|
||||
object? state,
|
||||
Func<object?, CancellationToken, ValueTask<string>> factory,
|
||||
Func<CancellationToken, ValueTask<object>> state,
|
||||
Func<Func<CancellationToken, ValueTask<object>>, CancellationToken, ValueTask<object>> factory,
|
||||
HybridCacheEntryOptions? options,
|
||||
IEnumerable<string>? tags,
|
||||
CancellationToken token) =>
|
||||
@@ -178,7 +179,7 @@ public class HybridCacheExtensionsTests
|
||||
});
|
||||
|
||||
// Act
|
||||
var (exists, value) = await HybridCacheExtensions.TryGetValueAsync<string>(_cacheMock.Object, key);
|
||||
var (exists, value) = await HybridCacheExtensions.TryGetValueAsync<object>(_cacheMock.Object, key, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(exists);
|
||||
|
||||
Reference in New Issue
Block a user