diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs
index e70f323f1c..42d3319661 100644
--- a/src/Umbraco.Core/Services/ContentService.cs
+++ b/src/Umbraco.Core/Services/ContentService.cs
@@ -372,7 +372,7 @@ public class ContentService : RepositoryService, IContentService
public IContent CreateAndSave(string name, int parentId, string contentTypeAlias, int userId = Constants.Security.SuperUserId)
{
// TODO: what about culture?
- using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
+ using (ICoreScope scope = ScopeProvider.CreateCoreScope())
{
// locking the content tree secures content types too
scope.WriteLock(Constants.Locks.ContentTree);
@@ -395,6 +395,8 @@ public class ContentService : RepositoryService, IContentService
Save(content, userId);
+ scope.Complete();
+
return content;
}
}
@@ -416,7 +418,7 @@ public class ContentService : RepositoryService, IContentService
throw new ArgumentNullException(nameof(parent));
}
- using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
+ using (ICoreScope scope = ScopeProvider.CreateCoreScope())
{
// locking the content tree secures content types too
scope.WriteLock(Constants.Locks.ContentTree);
@@ -431,6 +433,7 @@ public class ContentService : RepositoryService, IContentService
Save(content, userId);
+ scope.Complete();
return content;
}
}
@@ -508,10 +511,11 @@ public class ContentService : RepositoryService, IContentService
///
public void PersistContentSchedule(IContent content, ContentScheduleCollection contentSchedule)
{
- using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
+ using (ICoreScope scope = ScopeProvider.CreateCoreScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
_documentRepository.PersistContentSchedule(content, contentSchedule);
+ scope.Complete();
}
}
@@ -1951,7 +1955,7 @@ public class ContentService : RepositoryService, IContentService
cultures = new HashSet(); // empty means 'already published'
}
- if (isRoot || edited)
+ if (edited)
{
cultures.Add(c); // means 'republish this culture'
}
@@ -2106,13 +2110,11 @@ public class ContentService : RepositoryService, IContentService
}
// deal with the branch root - if it fails, abort
- var rootPublishNotificationState = new Dictionary();
- PublishResult? rootResult = SaveAndPublishBranchItem(scope, document, shouldPublish, publishCultures, true,
- publishedDocuments, eventMessages, userId, allLangs, rootPublishNotificationState);
- if (rootResult != null)
+ PublishResult? result = SaveAndPublishBranchItem(scope, document, shouldPublish, publishCultures, true, publishedDocuments, eventMessages, userId, allLangs, out IDictionary notificationState);
+ if (result != null)
{
- results.Add(rootResult);
- if (!rootResult.Success)
+ results.Add(result);
+ if (!result.Success)
{
return results;
}
@@ -2125,7 +2127,6 @@ public class ContentService : RepositoryService, IContentService
int count;
var page = 0;
const int pageSize = 100;
- PublishResult? result = null;
do
{
count = 0;
@@ -2144,8 +2145,7 @@ public class ContentService : RepositoryService, IContentService
}
// no need to check path here, parent has to be published here
- result = SaveAndPublishBranchItem(scope, d, shouldPublish, publishCultures, false,
- publishedDocuments, eventMessages, userId, allLangs,null);
+ result = SaveAndPublishBranchItem(scope, d, shouldPublish, publishCultures, false, publishedDocuments, eventMessages, userId, allLangs, out _);
if (result != null)
{
results.Add(result);
@@ -2169,12 +2169,7 @@ public class ContentService : RepositoryService, IContentService
// (SaveAndPublishBranchOne does *not* do it)
scope.Notifications.Publish(
new ContentTreeChangeNotification(document, TreeChangeTypes.RefreshBranch, eventMessages));
- if (rootResult?.Success is true)
- {
- scope.Notifications.Publish(
- new ContentPublishedNotification(rootResult!.Content!.Yield(), eventMessages, true)
- .WithState(rootPublishNotificationState));
- }
+ scope.Notifications.Publish(new ContentPublishedNotification(publishedDocuments, eventMessages).WithState(notificationState));
scope.Complete();
}
@@ -2185,9 +2180,6 @@ public class ContentService : RepositoryService, IContentService
// shouldPublish: a function determining whether the document has changes that need to be published
// note - 'force' is handled by 'editing'
// publishValues: a function publishing values (using the appropriate PublishCulture calls)
- /// Only set this when processing a the root of the branch
- /// Published notification will not be send when this property is set
- ///
private PublishResult? SaveAndPublishBranchItem(
ICoreScope scope,
IContent document,
@@ -2199,8 +2191,9 @@ public class ContentService : RepositoryService, IContentService
EventMessages evtMsgs,
int userId,
IReadOnlyCollection allLangs,
- IDictionary? rootPublishingNotificationState)
+ out IDictionary notificationState)
{
+ notificationState = new Dictionary();
HashSet? culturesToPublish = shouldPublish(document);
// null = do not include
@@ -2228,17 +2221,11 @@ public class ContentService : RepositoryService, IContentService
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, document);
}
- var notificationState = rootPublishingNotificationState ?? new Dictionary();
- PublishResult result = CommitDocumentChangesInternal(scope, document, evtMsgs, allLangs, notificationState, userId, true, isRoot);
- if (!result.Success)
+ PublishResult result = CommitDocumentChangesInternal(scope, document, evtMsgs, allLangs, savingNotification.State, userId, true, isRoot);
+ if (result.Success)
{
- return result;
- }
-
- publishedDocuments.Add(document);
- if (rootPublishingNotificationState == null)
- {
- scope.Notifications.Publish(new ContentPublishedNotification(result.Content!, evtMsgs).WithState(notificationState));
+ publishedDocuments.Add(document);
+ notificationState = savingNotification.State;
}
return result;
@@ -2982,7 +2969,7 @@ public class ContentService : RepositoryService, IContentService
public ContentDataIntegrityReport CheckDataIntegrity(ContentDataIntegrityReportOptions options)
{
- using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
+ using (ICoreScope scope = ScopeProvider.CreateCoreScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
@@ -2995,6 +2982,8 @@ public class ContentService : RepositoryService, IContentService
scope.Notifications.Publish(new ContentTreeChangeNotification(root, TreeChangeTypes.RefreshAll, EventMessagesFactory.Get()));
}
+ scope.Complete();
+
return report;
}
}
diff --git a/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs
index 6f2b2ddef5..a645e6222e 100644
--- a/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs
+++ b/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs
@@ -354,7 +354,6 @@ public abstract class ContentTypeServiceBase : ContentTypeSe
}
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
-
{
scope.ReadLock(ReadLockIds);
return Repository.GetMany(ids.ToArray());
diff --git a/src/Umbraco.Core/Services/ContentVersionService.cs b/src/Umbraco.Core/Services/ContentVersionService.cs
index 6f1c28deb8..1c190311da 100644
--- a/src/Umbraco.Core/Services/ContentVersionService.cs
+++ b/src/Umbraco.Core/Services/ContentVersionService.cs
@@ -68,7 +68,7 @@ internal class ContentVersionService : IContentVersionService
///
public void SetPreventCleanup(int versionId, bool preventCleanup, int userId = Constants.Security.SuperUserId)
{
- using (ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true))
+ using (ICoreScope scope = _scopeProvider.CreateCoreScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
_documentVersionRepository.SetPreventCleanup(versionId, preventCleanup);
@@ -87,6 +87,7 @@ internal class ContentVersionService : IContentVersionService
var message = $"set preventCleanup = '{preventCleanup}' for version '{versionId}'";
Audit(auditType, userId, version.ContentId, message, $"{version.VersionDate}");
+ scope.Complete();
}
}
@@ -120,7 +121,7 @@ internal class ContentVersionService : IContentVersionService
*
* tl;dr lots of scopes to enable other connections to use the DB whilst we work.
*/
- using (ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true))
+ using (ICoreScope scope = _scopeProvider.CreateCoreScope())
{
IReadOnlyCollection? allHistoricVersions =
_documentVersionRepository.GetDocumentVersionsEligibleForCleanup();
@@ -154,6 +155,8 @@ internal class ContentVersionService : IContentVersionService
versionsToDelete.Add(version);
}
+
+ scope.Complete();
}
if (!versionsToDelete.Any())
@@ -169,7 +172,7 @@ internal class ContentVersionService : IContentVersionService
foreach (IEnumerable group in versionsToDelete.InGroupsOf(Constants.Sql.MaxParameterCount))
{
- using (ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true))
+ using (ICoreScope scope = _scopeProvider.CreateCoreScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
var groupEnumerated = group.ToList();
@@ -182,12 +185,16 @@ internal class ContentVersionService : IContentVersionService
scope.Notifications.Publish(
new ContentDeletedVersionsNotification(version.ContentId, messages, version.VersionId));
}
+
+ scope.Complete();
}
}
- using (_scopeProvider.CreateCoreScope(autoComplete: true))
+ using (ICoreScope scope = _scopeProvider.CreateCoreScope())
{
Audit(AuditType.Delete, Constants.Security.SuperUserId, -1, $"Removed {versionsToDelete.Count} ContentVersion(s) according to cleanup policy");
+
+ scope.Complete();
}
return versionsToDelete;
diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs
index 99d8c225c3..c27c72a6d8 100644
--- a/src/Umbraco.Core/Services/DataTypeService.cs
+++ b/src/Umbraco.Core/Services/DataTypeService.cs
@@ -680,7 +680,7 @@ namespace Umbraco.Cms.Core.Services.Implement
[Obsolete("Please use GetReferencesAsync. Will be deleted in V15.")]
public IReadOnlyDictionary> GetReferences(int id)
{
- using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete:true);
+ using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
return _dataTypeRepository.FindUsages(id);
}
diff --git a/src/Umbraco.Core/Services/DefaultContentVersionCleanupPolicy.cs b/src/Umbraco.Core/Services/DefaultContentVersionCleanupPolicy.cs
index f51858fa5b..234dae683f 100644
--- a/src/Umbraco.Core/Services/DefaultContentVersionCleanupPolicy.cs
+++ b/src/Umbraco.Core/Services/DefaultContentVersionCleanupPolicy.cs
@@ -33,7 +33,7 @@ public class DefaultContentVersionCleanupPolicy : IContentVersionCleanupPolicy
var theRest = new List();
- using (_scopeProvider.CreateCoreScope(autoComplete: true))
+ using (ICoreScope scope = _scopeProvider.CreateCoreScope())
{
var policyOverrides = _documentVersionRepository.GetCleanupPolicies()?
.ToDictionary(x => x.ContentTypeId);
@@ -77,6 +77,8 @@ public class DefaultContentVersionCleanupPolicy : IContentVersionCleanupPolicy
yield return version;
}
}
+
+ scope.Complete();
}
}
diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs
index 26731b9393..7aba44d16b 100644
--- a/src/Umbraco.Core/Services/MediaService.cs
+++ b/src/Umbraco.Core/Services/MediaService.cs
@@ -1199,7 +1199,7 @@ namespace Umbraco.Cms.Core.Services
public ContentDataIntegrityReport CheckDataIntegrity(ContentDataIntegrityReportOptions options)
{
- using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
+ using (ICoreScope scope = ScopeProvider.CreateCoreScope())
{
scope.WriteLock(Constants.Locks.MediaTree);
@@ -1212,6 +1212,7 @@ namespace Umbraco.Cms.Core.Services
scope.Notifications.Publish(new MediaTreeChangeNotification(root, TreeChangeTypes.RefreshAll, EventMessagesFactory.Get()));
}
+ scope.Complete();
return report;
}
}
diff --git a/src/Umbraco.Core/Services/TwoFactorLoginService.cs b/src/Umbraco.Core/Services/TwoFactorLoginService.cs
index 64da30b991..bcfa102d1f 100644
--- a/src/Umbraco.Core/Services/TwoFactorLoginService.cs
+++ b/src/Umbraco.Core/Services/TwoFactorLoginService.cs
@@ -42,8 +42,10 @@ public class TwoFactorLoginService : ITwoFactorLoginService
///
public async Task DeleteUserLoginsAsync(Guid userOrMemberKey)
{
- using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
+ using ICoreScope scope = _scopeProvider.CreateCoreScope();
await _twoFactorLoginRepository.DeleteUserLoginsAsync(userOrMemberKey);
+
+ scope.Complete();
}
///
@@ -138,8 +140,12 @@ public class TwoFactorLoginService : ITwoFactorLoginService
///
public async Task DisableAsync(Guid userOrMemberKey, string providerName)
{
- using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
- return await _twoFactorLoginRepository.DeleteUserLoginsAsync(userOrMemberKey, providerName);
+ using ICoreScope scope = _scopeProvider.CreateCoreScope();
+ var result = await _twoFactorLoginRepository.DeleteUserLoginsAsync(userOrMemberKey, providerName);
+
+ scope.Complete();
+
+ return result;
}
///
@@ -156,9 +162,10 @@ public class TwoFactorLoginService : ITwoFactorLoginService
///
public Task SaveAsync(TwoFactorLogin twoFactorLogin)
{
- using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
+ using ICoreScope scope = _scopeProvider.CreateCoreScope();
_twoFactorLoginRepository.Save(twoFactorLogin);
+ scope.Complete();
return Task.CompletedTask;
}
diff --git a/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs b/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs
index 328cf5ee5c..da1fbaf157 100644
--- a/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs
+++ b/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs
@@ -106,7 +106,7 @@ public class ScheduledPublishing : RecurringHostedServiceBase
// but then what should be its "scope"? could we attach it to scopes?
// - and we should definitively *not* have to flush it here (should be auto)
using UmbracoContextReference contextReference = _umbracoContextFactory.EnsureUmbracoContext();
- using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
+ using ICoreScope scope = _scopeProvider.CreateCoreScope();
/* We used to assume that there will never be two instances running concurrently where (IsMainDom && ServerRole == SchedulingPublisher)
* However this is possible during an azure deployment slot swap for the SchedulingPublisher instance when trying to achieve zero downtime deployments.
@@ -125,6 +125,8 @@ public class ScheduledPublishing : RecurringHostedServiceBase
grouped.Count(),
grouped.Key);
}
+
+ scope.Complete();
}
finally
{
diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerConfig.cs b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerConfig.cs
index 96be41d6fe..53ecef211a 100644
--- a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerConfig.cs
+++ b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerConfig.cs
@@ -1,6 +1,8 @@
+using Microsoft.Extensions.DependencyInjection;
+using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Persistence.Repositories;
-using Umbraco.Cms.Core.Scoping;
+using Umbraco.Cms.Infrastructure.Scoping;
using IScope = Umbraco.Cms.Infrastructure.Scoping.IScope;
namespace Umbraco.Cms.Core.Logging.Viewer;
@@ -10,6 +12,21 @@ public class LogViewerConfig : ILogViewerConfig
private readonly ILogViewerQueryRepository _logViewerQueryRepository;
private readonly IScopeProvider _scopeProvider;
+ [Obsolete("Use non-obsolete ctor. This will be removed in Umbraco 14.")]
+ public LogViewerConfig(ILogViewerQueryRepository logViewerQueryRepository, Umbraco.Cms.Core.Scoping.IScopeProvider scopeProvider)
+ : this(logViewerQueryRepository, StaticServiceProvider.Instance.GetRequiredService())
+ {
+
+ }
+
+ //Temp ctor used by MSDI (Greedy)
+ [Obsolete("Use non-obsolete ctor. This will be removed in Umbraco 14.")]
+ public LogViewerConfig(ILogViewerQueryRepository logViewerQueryRepository, Umbraco.Cms.Core.Scoping.IScopeProvider coreScopeProvider, IScopeProvider scopeProvider)
+ : this(logViewerQueryRepository, scopeProvider)
+ {
+
+ }
+
public LogViewerConfig(ILogViewerQueryRepository logViewerQueryRepository, IScopeProvider scopeProvider)
{
_logViewerQueryRepository = logViewerQueryRepository;
@@ -28,9 +45,10 @@ public class LogViewerConfig : ILogViewerConfig
[Obsolete("Use ILogViewerService.AddSavedLogQueryAsync instead. Scheduled for removal in Umbraco 15.")]
public IReadOnlyList AddSavedSearch(string name, string query)
{
- using IScope scope = _scopeProvider.CreateScope(autoComplete: true);
+ using IScope scope = _scopeProvider.CreateScope();
_logViewerQueryRepository.Save(new LogViewerQuery(name, query));
+ scope.Complete();
return GetSavedSearches();
}
@@ -40,7 +58,7 @@ public class LogViewerConfig : ILogViewerConfig
[Obsolete("Use ILogViewerService.DeleteSavedLogQueryAsync instead. Scheduled for removal in Umbraco 15.")]
public IReadOnlyList DeleteSavedSearch(string name)
{
- using IScope scope = _scopeProvider.CreateScope(autoComplete: true);
+ using IScope scope = _scopeProvider.CreateScope();
ILogViewerQuery? item = _logViewerQueryRepository.GetByName(name);
if (item is not null)
@@ -49,6 +67,8 @@ public class LogViewerConfig : ILogViewerConfig
}
// Return the updated object - so we can instantly reset the entire array from the API response
- return GetSavedSearches();
+ IReadOnlyList result = GetSavedSearches();
+ scope.Complete();
+ return result;
}
}
diff --git a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs
index 6e1931997a..fcc87dacf3 100644
--- a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs
+++ b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs
@@ -96,7 +96,7 @@ public class MemberUserStore : UmbracoUserStore 0)
{
+ // the external user might actually be signed in at this point, but certain errors (i.e. missing claims)
+ // prevents us from applying said user to a back-office session. make sure the sign-in manager does not
+ // report the user as being signed in for subsequent requests.
+ await _signInManager.SignOutAsync();
+
ViewData.SetExternalSignInProviderErrors(
new BackOfficeExternalLoginProviderErrors(
loginInfo.LoginProvider,
diff --git a/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs b/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs
index e59d3166bb..2e48fbf25d 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs
@@ -188,15 +188,41 @@ public class ExamineManagementController : UmbracoAuthorizedJsonController
private ExamineIndexModel CreateModel(IIndex index)
{
var indexName = index.Name;
-
IIndexDiagnostics indexDiag = _indexDiagnosticsFactory.Create(index);
-
Attempt isHealth = indexDiag.IsHealthy();
+ var healthResult = isHealth.Result;
+
+ long documentCount;
+ int fieldCount;
+
+ try
+ {
+ // This will throw if the index is corrupted - i.e. a file in the index folder cannot be found
+ // Which will break the UI and not give the possibility to rebuild the index
+ documentCount = indexDiag.GetDocumentCount();
+ fieldCount = indexDiag.GetFieldNames().Count();
+ }
+ catch (FileNotFoundException ex)
+ {
+ // Safe catch that will allow to rebuild a corrupted index
+ documentCount = 0;
+ fieldCount = 0;
+
+ _logger.LogWarning(ex, "{name} is corrupted.", indexName);
+
+ if (!string.IsNullOrWhiteSpace(healthResult))
+ {
+ healthResult += " ";
+ }
+
+ // Provide a useful message in the Examine dashboard
+ healthResult += $"It may not be possible to rebuild the index. Please try deleting the entire {indexName} folder and then attempt to rebuild it again.";
+ }
var properties = new Dictionary
{
- ["DocumentCount"] = indexDiag.GetDocumentCount(),
- ["FieldCount"] = indexDiag.GetFieldNames().Count()
+ ["DocumentCount"] = documentCount,
+ ["FieldCount"] = fieldCount
};
foreach (KeyValuePair p in indexDiag.Metadata)
@@ -207,7 +233,7 @@ public class ExamineManagementController : UmbracoAuthorizedJsonController
var indexerModel = new ExamineIndexModel
{
Name = indexName,
- HealthStatus = isHealth.Success ? isHealth.Result ?? "Healthy" : isHealth.Result ?? "Unhealthy",
+ HealthStatus = isHealth.Success ? healthResult ?? "Healthy" : healthResult ?? "Unhealthy",
ProviderProperties = properties,
CanRebuild = _indexRebuilder.CanRebuild(index.Name)
};
diff --git a/src/Umbraco.Web.Website/Controllers/UmbProfileController.cs b/src/Umbraco.Web.Website/Controllers/UmbProfileController.cs
index 41286f7dba..22d5f7e3e2 100644
--- a/src/Umbraco.Web.Website/Controllers/UmbProfileController.cs
+++ b/src/Umbraco.Web.Website/Controllers/UmbProfileController.cs
@@ -102,7 +102,7 @@ public class UmbProfileController : SurfaceController
private async Task UpdateMemberAsync(ProfileModel model, MemberIdentityUser currentMember)
{
- using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
+ using ICoreScope scope = _scopeProvider.CreateCoreScope();
currentMember.Email = model.Email;
currentMember.Name = model.Name;
@@ -140,6 +140,7 @@ public class UmbProfileController : SurfaceController
_memberService.Save(member);
+ scope.Complete();
return saveResult;
}
}
diff --git a/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs b/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs
index 493dd624d1..3fb2a966c4 100644
--- a/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs
+++ b/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs
@@ -118,7 +118,7 @@ public class UmbRegisterController : SurfaceController
/// Result of registration operation.
private async Task RegisterMemberAsync(RegisterModel model)
{
- using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
+ using ICoreScope scope = _scopeProvider.CreateCoreScope();
// U4-10762 Server error with "Register Member" snippet (Cannot save member with empty name)
// If name field is empty, add the email address instead.
@@ -160,6 +160,8 @@ public class UmbRegisterController : SurfaceController
}
}
+ scope.Complete();
+
return identityResult;
}
}
diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json
index 4608c7278d..a735ba8f7c 100644
--- a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json
+++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json
@@ -17,7 +17,7 @@
"xhr2": "^0.2.1"
},
"devDependencies": {
- "@playwright/test": "^1.35",
+ "@playwright/test": "^1.38",
"del": "^6.0.0",
"ncp": "^2.0.0",
"prompt": "^1.2.0",
@@ -86,22 +86,18 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.35.1",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.35.1.tgz",
- "integrity": "sha512-b5YoFe6J9exsMYg0pQAobNDR85T1nLumUYgUTtKm4d21iX2L7WqKq9dW8NGJ+2vX0etZd+Y7UeuqsxDXm9+5ZA==",
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.0.tgz",
+ "integrity": "sha512-xis/RXXsLxwThKnlIXouxmIvvT3zvQj1JE39GsNieMUrMpb3/GySHDh2j8itCG22qKVD4MYLBp7xB73cUW/UUw==",
"dev": true,
"dependencies": {
- "@types/node": "*",
- "playwright-core": "1.35.1"
+ "playwright": "1.38.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=16"
- },
- "optionalDependencies": {
- "fsevents": "2.3.2"
}
},
"node_modules/@sideway/address": {
@@ -744,10 +740,28 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/playwright": {
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.0.tgz",
+ "integrity": "sha512-fJGw+HO0YY+fU/F1N57DMO+TmXHTrmr905J05zwAQE9xkuwP/QLDk63rVhmyxh03dYnEhnRbsdbH9B0UVVRB3A==",
+ "dev": true,
+ "dependencies": {
+ "playwright-core": "1.38.0"
+ },
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
"node_modules/playwright-core": {
- "version": "1.35.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.35.1.tgz",
- "integrity": "sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==",
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz",
+ "integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
@@ -988,5 +1002,718 @@
"node": ">= 6"
}
}
+ },
+ "dependencies": {
+ "@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true
+ },
+ "@hapi/hoek": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
+ "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==",
+ "dev": true
+ },
+ "@hapi/topo": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
+ "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
+ "dev": true,
+ "requires": {
+ "@hapi/hoek": "^9.0.0"
+ }
+ },
+ "@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@playwright/test": {
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.0.tgz",
+ "integrity": "sha512-xis/RXXsLxwThKnlIXouxmIvvT3zvQj1JE39GsNieMUrMpb3/GySHDh2j8itCG22qKVD4MYLBp7xB73cUW/UUw==",
+ "dev": true,
+ "requires": {
+ "playwright": "1.38.0"
+ }
+ },
+ "@sideway/address": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
+ "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==",
+ "dev": true,
+ "requires": {
+ "@hapi/hoek": "^9.0.0"
+ }
+ },
+ "@sideway/formula": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
+ "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==",
+ "dev": true
+ },
+ "@sideway/pinpoint": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
+ "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "14.17.33",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.33.tgz",
+ "integrity": "sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g==",
+ "dev": true
+ },
+ "@umbraco/json-models-builders": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-1.0.6.tgz",
+ "integrity": "sha512-bXwfXcpuqG1Ye714L9KJEGXuSzJfckysE/6CuPjdG8FqHWTE1brv28teR2oMw+ih8ca2u2zUboRgdzLEU/1D3Q==",
+ "requires": {
+ "camelize": "^1.0.0",
+ "faker": "^4.1.0"
+ }
+ },
+ "@umbraco/playwright-testhelpers": {
+ "version": "1.0.25",
+ "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-1.0.25.tgz",
+ "integrity": "sha512-6H452J6LhP0EHjF4jR7V7i0U8WPTiAbSyhN1J459BbbYEJ4QX1A2ZlCdA6VSBAsK1xYdMXD+yxsVJq7AAwiy9A==",
+ "requires": {
+ "@umbraco/json-models-builders": "^1.0.6",
+ "camelize": "^1.0.0",
+ "faker": "^4.1.0",
+ "form-data": "^4.0.0",
+ "node-fetch": "^2.6.7",
+ "xhr2": "^0.2.1"
+ }
+ },
+ "aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ }
+ },
+ "array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true
+ },
+ "async": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz",
+ "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==",
+ "dev": true
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ },
+ "axios": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
+ "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
+ "dev": true,
+ "requires": {
+ "follow-redirects": "^1.14.7"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "camelize": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
+ "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
+ },
+ "clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+ "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "cycle": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
+ "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==",
+ "dev": true
+ },
+ "del": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz",
+ "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==",
+ "dev": true,
+ "requires": {
+ "globby": "^11.0.1",
+ "graceful-fs": "^4.2.4",
+ "is-glob": "^4.0.1",
+ "is-path-cwd": "^2.2.0",
+ "is-path-inside": "^3.0.2",
+ "p-map": "^4.0.0",
+ "rimraf": "^3.0.2",
+ "slash": "^3.0.0"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+ },
+ "dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "requires": {
+ "path-type": "^4.0.0"
+ }
+ },
+ "dotenv": {
+ "version": "16.0.2",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.2.tgz",
+ "integrity": "sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA=="
+ },
+ "eyes": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
+ "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==",
+ "dev": true
+ },
+ "faker": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz",
+ "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8="
+ },
+ "fast-glob": {
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ }
+ },
+ "fastq": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
+ "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "follow-redirects": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "dev": true
+ },
+ "form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "requires": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-path-cwd": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
+ "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
+ "dev": true
+ },
+ "is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true
+ },
+ "joi": {
+ "version": "17.6.3",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.3.tgz",
+ "integrity": "sha512-YlQsIaS9MHYekzf1Qe11LjTkNzx9qhYluK3172z38RxYoAUf82XMX1p1DG1H4Wtk2ED/vPdSn9OggqtDu+aTow==",
+ "dev": true,
+ "requires": {
+ "@hapi/hoek": "^9.0.0",
+ "@hapi/topo": "^5.0.0",
+ "@sideway/address": "^4.1.3",
+ "@sideway/formula": "^3.0.0",
+ "@sideway/pinpoint": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "mime-db": {
+ "version": "1.51.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
+ "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
+ },
+ "mime-types": {
+ "version": "2.1.34",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
+ "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+ "requires": {
+ "mime-db": "1.51.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+ "dev": true
+ },
+ "ncp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
+ "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==",
+ "dev": true
+ },
+ "node-fetch": {
+ "version": "2.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+ "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "requires": {
+ "aggregate-error": "^3.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true
+ },
+ "path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true
+ },
+ "playwright": {
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.0.tgz",
+ "integrity": "sha512-fJGw+HO0YY+fU/F1N57DMO+TmXHTrmr905J05zwAQE9xkuwP/QLDk63rVhmyxh03dYnEhnRbsdbH9B0UVVRB3A==",
+ "dev": true,
+ "requires": {
+ "fsevents": "2.3.2",
+ "playwright-core": "1.38.0"
+ }
+ },
+ "playwright-core": {
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz",
+ "integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g==",
+ "dev": true
+ },
+ "prompt": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.3.0.tgz",
+ "integrity": "sha512-ZkaRWtaLBZl7KKAKndKYUL8WqNT+cQHKRZnT4RYYms48jQkFw3rrBL+/N5K/KtdEveHkxs982MX2BkDKub2ZMg==",
+ "dev": true,
+ "requires": {
+ "@colors/colors": "1.5.0",
+ "async": "3.2.3",
+ "read": "1.0.x",
+ "revalidator": "0.1.x",
+ "winston": "2.x"
+ }
+ },
+ "queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true
+ },
+ "read": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
+ "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==",
+ "dev": true,
+ "requires": {
+ "mute-stream": "~0.0.4"
+ }
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
+ "revalidator": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz",
+ "integrity": "sha512-xcBILK2pA9oh4SiinPEZfhP8HfrB/ha+a2fTMyl7Om2WjlDVrOQy99N2MXXlUHqGJz4qEu2duXxHJjDWuK/0xg==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "rxjs": {
+ "version": "7.5.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz",
+ "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ },
+ "stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+ "dev": true
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "4.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz",
+ "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==",
+ "dev": true
+ },
+ "wait-on": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz",
+ "integrity": "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==",
+ "dev": true,
+ "requires": {
+ "axios": "^0.25.0",
+ "joi": "^17.6.0",
+ "lodash": "^4.17.21",
+ "minimist": "^1.2.5",
+ "rxjs": "^7.5.4"
+ }
+ },
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "winston": {
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.6.tgz",
+ "integrity": "sha512-J5Zu4p0tojLde8mIOyDSsmLmcP8I3Z6wtwpTDHx1+hGcdhxcJaAmG4CFtagkb+NiN1M9Ek4b42pzMWqfc9jm8w==",
+ "dev": true,
+ "requires": {
+ "async": "^3.2.3",
+ "colors": "1.0.x",
+ "cycle": "1.0.x",
+ "eyes": "0.1.x",
+ "isstream": "0.1.x",
+ "stack-trace": "0.0.x"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "xhr2": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.1.tgz",
+ "integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw=="
+ }
}
}
diff --git a/tests/Umbraco.Tests.AcceptanceTest/package.json b/tests/Umbraco.Tests.AcceptanceTest/package.json
index e13dc2591d..5d861a0ae6 100644
--- a/tests/Umbraco.Tests.AcceptanceTest/package.json
+++ b/tests/Umbraco.Tests.AcceptanceTest/package.json
@@ -10,7 +10,9 @@
"createTest": "node createTest.js"
},
"devDependencies": {
- "@playwright/test": "^1.35",
+ "@playwright/test": "^1.38",
+ "typescript": "^4.8.3",
+ "tslib": "^2.4.0",
"del": "^6.0.0",
"ncp": "^2.0.0",
"prompt": "^1.2.0",
diff --git a/tests/Umbraco.Tests.Common/TestHelperBase.cs b/tests/Umbraco.Tests.Common/TestHelperBase.cs
index aa896bb7f0..844a25e8c6 100644
--- a/tests/Umbraco.Tests.Common/TestHelperBase.cs
+++ b/tests/Umbraco.Tests.Common/TestHelperBase.cs
@@ -7,6 +7,7 @@ using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Options;
using Moq;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
@@ -14,6 +15,8 @@ using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Diagnostics;
+using Umbraco.Cms.Core.DistributedLocking;
+using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Logging;
@@ -24,6 +27,8 @@ using Umbraco.Cms.Core.Runtime;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Strings;
using Umbraco.Cms.Infrastructure.Persistence;
+using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax;
+using Umbraco.Cms.Infrastructure.Scoping;
using Umbraco.Cms.Infrastructure.Serialization;
using Umbraco.Cms.Tests.Common.TestHelpers;
using Umbraco.Extensions;
@@ -76,6 +81,61 @@ public abstract class TestHelperBase
public IShortStringHelper ShortStringHelper { get; } =
new DefaultShortStringHelper(new DefaultShortStringHelperConfig());
+ public IScopeProvider ScopeProvider
+ {
+ get
+ {
+ var loggerFactory = NullLoggerFactory.Instance;
+ var fileSystems = new FileSystems(
+ loggerFactory,
+ Mock.Of(),
+ Mock.Of>(),
+ Mock.Of());
+ var mediaFileManager = new MediaFileManager(
+ Mock.Of(),
+ Mock.Of(),
+ loggerFactory.CreateLogger(),
+ Mock.Of(),
+ Mock.Of(),
+ Options.Create(new ContentSettings()));
+ var databaseFactory = new Mock();
+ var database = new Mock();
+ var sqlContext = new Mock();
+
+ var lockingMechanism = new Mock();
+ lockingMechanism.Setup(x => x.ReadLock(It.IsAny(), It.IsAny()))
+ .Returns(Mock.Of());
+ lockingMechanism.Setup(x => x.WriteLock(It.IsAny(), It.IsAny()))
+ .Returns(Mock.Of());
+
+ var lockingMechanismFactory = new Mock();
+ lockingMechanismFactory.Setup(x => x.DistributedLockingMechanism)
+ .Returns(lockingMechanism.Object);
+
+ // Setup mock of database factory to return mock of database.
+ databaseFactory.Setup(x => x.CreateDatabase()).Returns(database.Object);
+ databaseFactory.Setup(x => x.SqlContext).Returns(sqlContext.Object);
+
+ // Setup mock of database to return mock of sql SqlContext
+ database.Setup(x => x.SqlContext).Returns(sqlContext.Object);
+
+ var syntaxProviderMock = new Mock();
+
+ // Setup mock of ISqlContext to return syntaxProviderMock
+ sqlContext.Setup(x => x.SqlSyntax).Returns(syntaxProviderMock.Object);
+
+ return new ScopeProvider(
+ new AmbientScopeStack(),
+ new AmbientScopeContextStack(),
+ lockingMechanismFactory.Object,
+ databaseFactory.Object,
+ fileSystems,
+ new TestOptionsMonitor(new CoreDebugSettings()),
+ mediaFileManager,
+ loggerFactory,
+ Mock.Of());
+ }
+ }
public IJsonSerializer JsonSerializer { get; } = new JsonNetSerializer();
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServicePublishBranchTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServicePublishBranchTests.cs
index 023cde3625..2f74c6c979 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServicePublishBranchTests.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServicePublishBranchTests.cs
@@ -92,7 +92,7 @@ public class ContentServicePublishBranchTests : UmbracoIntegrationTest
AssertPublishResults(
r,
x => x.Result,
- PublishResultType.SuccessPublish, // During branch publishing, the change detection of the root branch runs AFTER the check to process the branchItem => root is always saved&Published as the intent requires it.
+ PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublishAlready);
@@ -139,7 +139,7 @@ public class ContentServicePublishBranchTests : UmbracoIntegrationTest
AssertPublishResults(
r,
x => x.Result,
- PublishResultType.SuccessPublish, // During branch publishing, the change detection of the root branch runs AFTER the check to process the branchItem => root is always saved&Published as the intent requires it.
+ PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublish,
@@ -184,7 +184,7 @@ public class ContentServicePublishBranchTests : UmbracoIntegrationTest
var r = ContentService.SaveAndPublishBranch(vRoot, false)
.ToArray(); // no culture specified so "*" is used, so all cultures
- Assert.AreEqual(PublishResultType.SuccessPublishCulture, r[0].Result); // During branch publishing, the change detection of the root branch runs AFTER the check to process the branchItem => root is always saved&Published as the intent requires it.
+ Assert.AreEqual(PublishResultType.SuccessPublishAlready, r[0].Result);
Assert.AreEqual(PublishResultType.SuccessPublishCulture, r[1].Result);
}
@@ -220,7 +220,7 @@ public class ContentServicePublishBranchTests : UmbracoIntegrationTest
var saveResult = ContentService.Save(iv1);
var r = ContentService.SaveAndPublishBranch(vRoot, false, "de").ToArray();
- Assert.AreEqual(PublishResultType.SuccessPublishCulture, r[0].Result); // During branch publishing, the change detection of the root branch runs AFTER the check to process the branchItem => root is always saved&Published as the intent requires it.
+ Assert.AreEqual(PublishResultType.SuccessPublishAlready, r[0].Result);
Assert.AreEqual(PublishResultType.SuccessPublishCulture, r[1].Result);
}
@@ -380,7 +380,7 @@ public class ContentServicePublishBranchTests : UmbracoIntegrationTest
AssertPublishResults(
r,
x => x.Result,
- PublishResultType.SuccessPublish, // During branch publishing, the change detection of the root branch runs AFTER the check to process the branchItem => root is always saved&Published as the intent requires it.
+ PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublish,
PublishResultType.SuccessPublishCulture);
@@ -406,7 +406,7 @@ public class ContentServicePublishBranchTests : UmbracoIntegrationTest
AssertPublishResults(
r,
x => x.Result,
- PublishResultType.SuccessPublish, // During branch publishing, the change detection of the root branch runs AFTER the check to process the branchItem => root is always saved&Published as the intent requires it.
+ PublishResultType.SuccessPublishAlready,
PublishResultType.SuccessPublish,
PublishResultType.SuccessPublishCulture);
diff --git a/tests/Umbraco.Tests.UnitTests/TestHelpers/TestHelper.cs b/tests/Umbraco.Tests.UnitTests/TestHelpers/TestHelper.cs
index 81e027b7c1..4468587866 100644
--- a/tests/Umbraco.Tests.UnitTests/TestHelpers/TestHelper.cs
+++ b/tests/Umbraco.Tests.UnitTests/TestHelpers/TestHelper.cs
@@ -31,6 +31,7 @@ using Umbraco.Cms.Core.Net;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Runtime;
+using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Strings;
using Umbraco.Cms.Infrastructure.Mail;
@@ -42,6 +43,7 @@ using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Extensions;
using File = System.IO.File;
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
+using IScopeProvider = Umbraco.Cms.Infrastructure.Scoping.IScopeProvider;
namespace Umbraco.Cms.Tests.UnitTests.TestHelpers;
@@ -58,6 +60,8 @@ public static class TestHelper
/// The assembly directory.
public static string WorkingDirectory => s_testHelperInternal.WorkingDirectory;
+ public static IScopeProvider ScopeProvider => s_testHelperInternal.ScopeProvider;
+ public static ICoreScopeProvider CoreScopeProvider => s_testHelperInternal.ScopeProvider;
public static IShortStringHelper ShortStringHelper => s_testHelperInternal.ShortStringHelper;
public static IJsonSerializer JsonSerializer => s_testHelperInternal.JsonSerializer;
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PropertyCacheVarianceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PropertyCacheVarianceTests.cs
new file mode 100644
index 0000000000..72a68b443c
--- /dev/null
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PropertyCacheVarianceTests.cs
@@ -0,0 +1,381 @@
+using Moq;
+using NUnit.Framework;
+using Umbraco.Cms.Core.Cache;
+using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.PublishedContent;
+using Umbraco.Cms.Core.PropertyEditors;
+using Umbraco.Cms.Core.PublishedCache;
+using Umbraco.Cms.Infrastructure.PublishedCache;
+using Umbraco.Cms.Infrastructure.PublishedCache.DataSource;
+using Property = Umbraco.Cms.Infrastructure.PublishedCache.Property;
+
+namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Published;
+
+[TestFixture]
+public class PropertyCacheVarianceTests
+{
+ // This class tests various permutations of property value calculation across variance types and cache levels.
+ //
+ // Properties contain different "value levels", all of which are cached:
+ // 1. The source value => the "raw" value from the client side editor (it can be different, but it's easiest to think of it like that).
+ // 2. The intermediate value => a "temporary" value that is used to calculate the various "final" values.
+ // 3. The object value => the "final" object value that is exposed in an IPublishedElement output.
+ // 4. The XPath value => a legacy "final" value, don't think too hard on it.
+ // 3. The delivery API object value => the "final" object value that is exposed in the Delivery API.
+ //
+ // Property values are cached based on a few rules:
+ // 1. The property type variation and the parent content type variation determines how the intermediate value is cached.
+ // The effective property variation is a product of both variations, meaning the property type and the content type
+ // variations are combined in an OR.
+ // The rules are as follows:
+ // - ContentVariation.Nothing => the intermediate value is calculated once and reused across all variants (cultures and segments).
+ // - ContentVariation.Culture => the intermediate value is calculated per culture and reused across all segments.
+ // - ContentVariation.Segment => the intermediate value is calculated per segment and reused across all cultures.
+ // - ContentVariation.CultureAndSegment => the intermediate value is calculated for all invoked culture and segment combinations.
+ // 2. The property type cache level (which is usually derived from the property value converter).
+ // - PropertyCacheLevel.Element => the final values are cached until the parent content item is updated.
+ // - PropertyCacheLevel.Elements => the final values are cached until the _any_ content item is updated.
+ // - PropertyCacheLevel.Snapshot => the final values are cached for the duration of the active cache snapshot (i.e. until the end of the current request).
+ // - PropertyCacheLevel.None => the final values are never cached and will be re-calculated each time they're requested.
+
+ // ### Invariant content type + invariant property type ###
+ [TestCase(
+ ContentVariation.Nothing,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.Element,
+ // no variation => the intermediate value is calculated only once
+ // cache level => the final value is calculated only once
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)")]
+ [TestCase(
+ ContentVariation.Nothing,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.Elements,
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)")]
+ [TestCase(
+ ContentVariation.Nothing,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.Snapshot,
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)")]
+ [TestCase(
+ ContentVariation.Nothing,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.None,
+ // no variation => the intermediate value is calculated once
+ // no cache => the final value is calculated for each request (reflects both changes in culture and segments)
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (da-DK:segment1)",
+ "en-US:segment2 (da-DK:segment1)",
+ "da-DK:segment2 (da-DK:segment1)")]
+ // ### Culture variant content type + invariant property type ###
+ [TestCase(
+ ContentVariation.Culture,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.Element,
+ // culture variation => the intermediate value is calculated per culture (ignores segment changes until a culture changes)
+ // cache level => the final value is calculated only once per culture (ignores segment changes until a culture changes)
+ // NOTE: in this test, culture changes before segment, so the updated segment is never reflected here
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "da-DK:segment1 (da-DK:segment1)")]
+ [TestCase(
+ ContentVariation.Culture,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.Elements,
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "da-DK:segment1 (da-DK:segment1)")]
+ [TestCase(
+ ContentVariation.Culture,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.Snapshot,
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "da-DK:segment1 (da-DK:segment1)")]
+ [TestCase(
+ ContentVariation.Culture,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.None,
+ // culture variation => the intermediate value is calculated per culture (ignores segment changes until a culture changes)
+ // no cache => the final value is calculated for each request (reflects both changes in culture and segments)
+ // NOTE: in this test, culture changes before segment, so the updated segment is never reflected in the intermediate value here
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment2 (en-US:segment1)",
+ "da-DK:segment2 (da-DK:segment1)")]
+ // NOTE: As the tests above show, cache levels Element, Elements and Snapshot all yield the same values in this
+ // test, because we are efficiently executing the test in a snapshot. From here on out we're only building
+ // test cases for Element and None.
+ // ### Segment variant content type + invariant property type ###
+ [TestCase(
+ ContentVariation.Segment,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.Element,
+ // segment variation => the intermediate value is calculated per segment (ignores culture changes until a segment changes)
+ // cache level => the final value is calculated only once per segment (ignores culture changes until a segment changes)
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "en-US:segment2 (en-US:segment2)")]
+ [TestCase(
+ ContentVariation.Segment,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.None,
+ // segment variation => the intermediate value is calculated per segment (ignores culture changes until a segment changes)
+ // no cache => the final value is calculated for each request (reflects both changes in culture and segments)
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (da-DK:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "da-DK:segment2 (en-US:segment2)")]
+ // ### Culture and segment variant content type + invariant property type ###
+ [TestCase(
+ ContentVariation.CultureAndSegment,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.Element,
+ // culture and segment variation => the intermediate value is calculated per culture and segment
+ // cache level => the final value is calculated only once per culture and segment (efficiently on every request in this test)
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "da-DK:segment2 (da-DK:segment2)")]
+ [TestCase(
+ ContentVariation.CultureAndSegment,
+ ContentVariation.Nothing,
+ PropertyCacheLevel.None,
+ // culture and segment variation => the intermediate value is calculated per culture and segment
+ // no cache => the final value is calculated for each request
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "da-DK:segment2 (da-DK:segment2)")]
+ // ### Invariant content type + culture variant property type ###
+ [TestCase(
+ ContentVariation.Nothing,
+ ContentVariation.Culture,
+ PropertyCacheLevel.Element,
+ // same behaviour as culture variation on content type + no variation on property type, see comments above
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "da-DK:segment1 (da-DK:segment1)")]
+ [TestCase(
+ ContentVariation.Nothing,
+ ContentVariation.Culture,
+ PropertyCacheLevel.None,
+ // same behaviour as culture variation on content type + no variation on property type, see comments above
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment2 (en-US:segment1)",
+ "da-DK:segment2 (da-DK:segment1)")]
+ // ### Invariant content type + segment variant property type ###
+ [TestCase(
+ ContentVariation.Nothing,
+ ContentVariation.Segment,
+ PropertyCacheLevel.Element,
+ // same behaviour as segment variation on content type + no variation on property type, see comments above
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "en-US:segment2 (en-US:segment2)")]
+ [TestCase(
+ ContentVariation.Nothing,
+ ContentVariation.Segment,
+ PropertyCacheLevel.None,
+ // same behaviour as segment variation on content type + no variation on property type, see comments above
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (da-DK:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "da-DK:segment2 (en-US:segment2)")]
+ // ### Invariant content type + culture and segment variant property type ###
+ [TestCase(
+ ContentVariation.Nothing,
+ ContentVariation.CultureAndSegment,
+ PropertyCacheLevel.Element,
+ // same behaviour as culture and segment variation on content type + no variation on property type, see comments above
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "da-DK:segment2 (da-DK:segment2)")]
+ [TestCase(
+ ContentVariation.Nothing,
+ ContentVariation.CultureAndSegment,
+ PropertyCacheLevel.None,
+ // same behaviour as culture and segment variation on content type + no variation on property type, see comments above
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "da-DK:segment2 (da-DK:segment2)")]
+ // ### Culture variant content type + segment variant property type ###
+ [TestCase(
+ ContentVariation.Culture,
+ ContentVariation.Segment,
+ PropertyCacheLevel.Element,
+ // same behaviour as culture and segment variation on content type + no variation on property type, see comments above
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "da-DK:segment2 (da-DK:segment2)")]
+ [TestCase(
+ ContentVariation.Culture,
+ ContentVariation.Segment,
+ PropertyCacheLevel.None,
+ // same behaviour as culture and segment variation on content type + no variation on property type, see comments above
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "da-DK:segment2 (da-DK:segment2)")]
+ public void ContentType_PropertyType_Variation_Cache_Values(
+ ContentVariation contentTypeVariation,
+ ContentVariation propertyTypeVariation,
+ PropertyCacheLevel propertyCacheLevel,
+ string expectedValue1DaDkSegment1,
+ string expectedValue2EnUsSegment1,
+ string expectedValue3EnUsSegment2,
+ string expectedValue4DaDkSegment2)
+ {
+ var variationContextCulture = "da-DK";
+ var variationContextSegment = "segment1";
+ var property = CreateProperty(
+ contentTypeVariation,
+ propertyTypeVariation,
+ propertyCacheLevel,
+ () => variationContextCulture,
+ () => variationContextSegment);
+
+ Assert.AreEqual(expectedValue1DaDkSegment1, property.GetValue());
+
+ variationContextCulture = "en-US";
+ Assert.AreEqual(expectedValue2EnUsSegment1, property.GetValue());
+
+ variationContextSegment = "segment2";
+ Assert.AreEqual(expectedValue3EnUsSegment2, property.GetValue());
+
+ variationContextCulture = "da-DK";
+ Assert.AreEqual(expectedValue4DaDkSegment2, property.GetValue());
+ }
+
+ [TestCase(
+ ContentVariation.Culture,
+ ContentVariation.Nothing,
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "da-DK:segment1 (da-DK:segment1)")]
+ [TestCase(
+ ContentVariation.Segment,
+ ContentVariation.Nothing,
+ "da-DK:segment1 (da-DK:segment1)",
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "en-US:segment2 (en-US:segment2)")]
+ [TestCase(
+ ContentVariation.Culture,
+ ContentVariation.Segment,
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "da-DK:segment2 (da-DK:segment2)")]
+ [TestCase(
+ ContentVariation.CultureAndSegment,
+ ContentVariation.Nothing,
+ "da-DK:segment1 (da-DK:segment1)",
+ "en-US:segment1 (en-US:segment1)",
+ "en-US:segment2 (en-US:segment2)",
+ "da-DK:segment2 (da-DK:segment2)")]
+ public void ContentType_PropertyType_Variation_Are_Interchangeable(
+ ContentVariation variation1,
+ ContentVariation variation2,
+ string expectedValue1DaDkSegment1,
+ string expectedValue2EnUsSegment1,
+ string expectedValue3EnUsSegment2,
+ string expectedValue4DaDkSegment2)
+ {
+ var scenarios = new[]
+ {
+ new { ContentTypeVariation = variation1, PropertyTypeVariation = variation2 },
+ new { ContentTypeVariation = variation2, PropertyTypeVariation = variation1 }
+ };
+
+ foreach (var scenario in scenarios)
+ {
+ var variationContextCulture = "da-DK";
+ var variationContextSegment = "segment1";
+ var property = CreateProperty(
+ scenario.ContentTypeVariation,
+ scenario.PropertyTypeVariation,
+ PropertyCacheLevel.Element,
+ () => variationContextCulture,
+ () => variationContextSegment);
+
+ Assert.AreEqual(expectedValue1DaDkSegment1, property.GetValue());
+
+ variationContextCulture = "en-US";
+ Assert.AreEqual(expectedValue2EnUsSegment1, property.GetValue());
+
+ variationContextSegment = "segment2";
+ Assert.AreEqual(expectedValue3EnUsSegment2, property.GetValue());
+
+ variationContextCulture = "da-DK";
+ Assert.AreEqual(expectedValue4DaDkSegment2, property.GetValue());
+ }
+ }
+
+ ///
+ /// Creates a new property with a mocked publishedSnapshotAccessor that uses a VariationContext that reads culture and segment information from the passed in functions.
+ ///
+ private Property CreateProperty(ContentVariation contentTypeVariation, ContentVariation propertyTypeVariation, PropertyCacheLevel propertyTypeCacheLevel, Func getCulture, Func getSegment)
+ {
+ var contentType = new Mock();
+ contentType.SetupGet(c => c.PropertyTypes).Returns(Array.Empty());
+ contentType.SetupGet(c => c.Variations).Returns(contentTypeVariation);
+
+ var contentNode = new ContentNode(123, Guid.NewGuid(), contentType.Object, 1, string.Empty, 1, 1, DateTime.Now, 1);
+ var contentData = new ContentData("bla", "bla", 1, DateTime.Now, 1, 1, true, new Dictionary(), null);
+
+ var elementCache = new FastDictionaryAppCache();
+ var snapshotCache = new FastDictionaryAppCache();
+ var publishedSnapshotMock = new Mock();
+ publishedSnapshotMock.SetupGet(p => p.ElementsCache).Returns(elementCache);
+ publishedSnapshotMock.SetupGet(p => p.SnapshotCache).Returns(snapshotCache);
+
+ var publishedSnapshot = publishedSnapshotMock.Object;
+ var publishedSnapshotAccessor = new Mock();
+ publishedSnapshotAccessor.Setup(p => p.TryGetPublishedSnapshot(out publishedSnapshot)).Returns(true);
+
+ var variationContextAccessorMock = new Mock();
+ variationContextAccessorMock
+ .SetupGet(mock => mock.VariationContext)
+ .Returns(() => new VariationContext(getCulture(), getSegment()));
+
+ var content = new PublishedContent(
+ contentNode,
+ contentData,
+ publishedSnapshotAccessor.Object,
+ variationContextAccessorMock.Object,
+ Mock.Of());
+
+ var propertyType = new Mock();
+ propertyType.SetupGet(p => p.CacheLevel).Returns(propertyTypeCacheLevel);
+ propertyType.SetupGet(p => p.DeliveryApiCacheLevel).Returns(propertyTypeCacheLevel);
+ propertyType.SetupGet(p => p.Variations).Returns(propertyTypeVariation);
+ propertyType
+ .Setup(p => p.ConvertSourceToInter(It.IsAny(), It.IsAny