Skip to content

Instantly share code, notes, and snippets.

@lars-erik
Last active October 30, 2019 20:03
Show Gist options
  • Save lars-erik/d6716f516153511b6461fc06abd53371 to your computer and use it in GitHub Desktop.
Save lars-erik/d6716f516153511b6461fc06abd53371 to your computer and use it in GitHub Desktop.
Umbracosupport 7.15
using System;
using System.Collections.Generic;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web;
namespace Umbraco.UnitTesting.Adapter.Support
{
public class FakeModelFactoryResolver : PublishedContentModelFactoryResolver
{
public FakeModelFactoryResolver(IPublishedContentModelFactory factory) : base(factory)
{
}
}
public class FakeContentModelFactory : IPublishedContentModelFactory
{
protected Dictionary<string, Func<IPublishedContent, IPublishedContent>> Factories { get; } = new Dictionary<string, Func<IPublishedContent, IPublishedContent>>();
protected Dictionary<Guid, IPublishedContent> items = new Dictionary<Guid, IPublishedContent>();
public void Register(string alias, Func<IPublishedContent, IPublishedContent> factory)
{
if (Factories.ContainsKey(alias))
Factories[alias] = factory;
else
Factories.Add(alias, factory);
}
public IPublishedContent CreateModel(IPublishedContent content)
{
if (items.ContainsKey(content.GetKey()))
return items[content.GetKey()];
if (Factories.ContainsKey(content.DocumentTypeAlias))
content = Factories[content.DocumentTypeAlias](content);
items.Add(content.GetKey(), content);
return content;
}
}
public static class ContentTypeHandler
{
public static Func<string, PublishedContentType> CreateDelegate = alias => new PublishedContentType(ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias));
public static PublishedContentType Create(string alias)
{
return CreateDelegate(alias);
}
public static void Init()
{
PublishedContentType.GetPublishedContentTypeCallback = Create;
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Moq;
using NUnit.Framework;
using umbraco.BusinessLogic;
using Umbraco.Core;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Mvc;
using Umbraco.Web.Routing;
namespace Umbraco.UnitTesting.Adapter.Support
{
public class UmbracoSupport : UmbracoSupport<IPublishedContent>
{
public UmbracoSupport()
: base(c => c)
{
}
}
public class UmbracoSupport<T> : BaseRoutingTest
where T : class, IPublishedContent
{
bool initialized = false;
private Func<IPublishedContent, T> contentFactory;
public UmbracoContext UmbracoContext => umbracoContext;
public new ServiceContext ServiceContext => serviceContext;
public T CurrentPage => currentPage;
public RouteData RouteData => routeData;
public UmbracoHelper UmbracoHelper => umbracoHelper;
public HttpContextBase HttpContext => umbracoContext.HttpContext;
public string ContentCacheXml { get; set; }
public string Url { get; private set; }
public PublishedContentRequest PublishedContentRequest => umbracoContext.PublishedContentRequest;
public IUmbracoSettingsSection Settings => settings;
public RoutingContext RoutingContext => routingContext;
public FakeContentModelFactory ModelFactory => fakeContentModelFactory;
public List<Type> ConverterTypes => converterTypes;
private List<Action> onInitialized = new List<Action>();
private IList<IContentType> contentTypes = new List<IContentType>();
public UmbracoSupport(Func<IPublishedContent, T> contentFactory)
{
this.contentFactory = contentFactory;
}
public void RegisterOnInitialized(Action action)
{
onInitialized.Add(action);
}
/// <summary>
/// Initializes a stubbed Umbraco request context. Generally called from [SetUp] methods.
/// Remember to call UmbracoSupport.DisposeUmbraco from your [TearDown].
/// </summary>
public void SetupUmbraco(string url = "http://localhost")
{
if (initialized)
throw new Exception("UmbracoSupport has already been initialized.");
var watch = new Stopwatch();
watch.Start();
Url = url;
InitializeFixture();
InitializeResolvers();
TryBaseInitialize();
InitializeSettings();
CreateRouteData();
CreateContexts();
onInitialized.ForEach(x => x());
CreateCurrentPage();
CreateHelper();
SetupContentTypeService();
InitializePublishedContentRequest();
watch.Stop();
Console.WriteLine("Umbraco init: " + watch.Elapsed);
InitializeHttpContext();
initialized = true;
}
protected override void FreezeResolution()
{
base.FreezeResolution();
var field = typeof(ResolverBase<LoggerResolver>).GetField("_resolver", BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Static);
field.SetValue(null, new LoggerResolver(Logger));
}
/// <summary>
/// Cleans up the stubbed Umbraco request context. Generally called from [TearDown] methods.
/// Must be called before another UmbracoSupport.SetupUmbraco.
/// </summary>
public void DisposeUmbraco()
{
if (!initialized)
throw new Exception("Cannot teardown non-initialized UmbracoSupport");
var field = typeof(ResolverBase<LoggerResolver>).GetField("_resolver", BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Static);
field.SetValue(null, null);
initialized = false;
typeof(StateHelper).GetField("_customHttpContext", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, null);
global::System.Web.HttpContext.Current = null;
TearDown();
}
public void SetContentFactory(Func<IPublishedContent, T> factory)
{
if (initialized)
throw new Exception("Can't set content factory after initialization");
this.contentFactory = factory;
}
private void InitializeHttpContext()
{
Assert.That(umbracoContext.HttpContext.Request.Cookies.Count, Is.EqualTo(1));
Assert.That(umbracoContext.HttpContext.Request.Cookies.AllKeys[0], Is.EqualTo("UMB_UCONTEXT"));
var responseCookies = new HttpCookieCollection();
var responseMock = Mock.Get(umbracoContext.HttpContext.Response);
Assert.That(umbracoContext.HttpContext.Response.Cookies, Is.Null);
responseMock.Setup(r => r.Cookies).Returns(() => responseCookies);
}
/// <summary>
/// Attaches the stubbed UmbracoContext et. al. to the Controller.
/// </summary>
/// <param name="controller"></param>
public void PrepareController(Controller controller)
{
var controllerContext = new ControllerContext(HttpContext, RouteData, controller);
controller.ControllerContext = controllerContext;
if (routeData.Values.ContainsKey("controller"))
{
return;
}
routeData.Values.Add("controller", controller.GetType().Name.Replace("Controller", ""));
routeData.Values.Add("action", "Dummy");
}
protected override string GetXmlContent(int templateId)
{
if (ContentCacheXml != null)
return ContentCacheXml;
return base.GetXmlContent(templateId);
}
private UmbracoContext umbracoContext;
private ServiceContext serviceContext;
private IUmbracoSettingsSection settings;
private RoutingContext routingContext;
private T currentPage;
private RouteData routeData;
private UmbracoHelper umbracoHelper;
private PublishedContentRequest publishedContentRequest;
private FakeContentModelFactory fakeContentModelFactory;
private readonly List<Type> converterTypes = new List<Type>();
protected override ApplicationContext CreateApplicationContext()
{
// Overrides the base CreateApplicationContext to inject a completely stubbed servicecontext
serviceContext = GetMockedServiceContext();
var appContext = new ApplicationContext(
new DatabaseContext(Mock.Of<IDatabaseFactory>(), Logger, SqlSyntax, GetDbProviderName()),
serviceContext,
CacheHelper,
ProfilingLogger);
return appContext;
}
public static ServiceContext GetMockedServiceContext()
{
return new ServiceContext(
new Mock<IContentService>().Object,
new Mock<IMediaService>().Object,
new Mock<IContentTypeService>().Object,
new Mock<IDataTypeService>().Object,
new Mock<IFileService>().Object,
new Mock<ILocalizationService>().Object,
new Mock<IPackagingService>().Object,
new Mock<IEntityService>().Object,
new Mock<IRelationService>().Object,
new Mock<IMemberGroupService>().Object,
new Mock<IMemberTypeService>().Object,
new Mock<IMemberService>().Object,
new Mock<IUserService>().Object,
new Mock<ISectionService>().Object,
new Mock<IApplicationTreeService>().Object,
new Mock<ITagService>().Object,
new Mock<INotificationService>().Object,
new Mock<ILocalizedTextService>().Object,
new Mock<IAuditService>().Object,
new Mock<IDomainService>().Object,
new Mock<ITaskService>().Object,
new Mock<IMacroService>().Object,
new Mock<IPublicAccessService>().Object,
new Mock<IExternalLoginService>().Object,
new Mock<IMigrationEntryService>().Object,
new Mock<IRedirectUrlService>().Object,
new Mock<IConsentService>().Object
);
}
private void InitializeResolvers()
{
UrlProviderResolver.Current = new UrlProviderResolver(
new ActivatorServiceProvider(),
Logger,
new[]
{
typeof(DefaultUrlProvider)
}
);
SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper());
fakeContentModelFactory = new FakeContentModelFactory();
PublishedContentModelFactoryResolver.Current = new FakeModelFactoryResolver(fakeContentModelFactory);
PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver(
new ActivatorServiceProvider(),
Logger,
converterTypes
);
}
private void TryBaseInitialize()
{
// Delegates to Umbraco.Tests initialization. Gives a nice hint about disposing the support class for each test.
try
{
Initialize();
}
catch (InvalidOperationException ex)
{
if (ex.Message.StartsWith("Resolution is frozen"))
throw new Exception("Resolution is frozen. This is probably because UmbracoSupport.DisposeUmbraco wasn't called before another UmbracoSupport.SetupUmbraco call.");
}
}
private void InitializeSettings()
{
// Stub up all the settings in Umbraco so we don't need a big app.config file.
settings = SettingsForTests.GenerateMockSettings();
SettingsForTests.ConfigureSettings(settings);
SettingsForTests.UseDirectoryUrls = true;
SettingsForTests.HideTopLevelNodeFromPath = true;
Mock.Get(Settings.RequestHandler).Setup(s => s.AddTrailingSlash).Returns(false);
}
private void CreateCurrentPage()
{
// Stubs up the content used as current page in all contexts
var publishedContent = Mock.Of<IPublishedContent>();
currentPage = (T)contentFactory?.Invoke(publishedContent) ?? (T)publishedContent;
}
private void CreateRouteData()
{
// Route data is used in many of the contexts, and might need more data throughout your tests.
routeData = new RouteData();
}
private void CreateContexts()
{
// Surface- and RenderMvcControllers need a routing context to fint the current content.
// Umbraco.Tests creates one and whips up the UmbracoContext in the process.
routingContext = GetRoutingContext(Url, -1, routeData, true, settings);
umbracoContext = routingContext.UmbracoContext;
}
protected new RoutingContext GetRoutingContext(string url, int templateId, RouteData routeData = null, bool setUmbracoContextCurrent = false, IUmbracoSettingsSection umbracoSettings = null)
{
var umbracoContext = this.GetUmbracoContext(url, templateId, routeData, false);
UrlProvider urlProvider = new UrlProvider(umbracoContext, umbracoSettings.WebRouting, new[]
{
new DefaultUrlProvider(umbracoSettings.RequestHandler) as IUrlProvider
})
{
Mode = UrlProviderMode.Absolute
};
var routingContext = new RoutingContext(umbracoContext, Enumerable.Empty<IContentFinder>(), Mock.Of<IContentFinder>(), urlProvider);
umbracoContext.RoutingContext = routingContext;
if (setUmbracoContextCurrent)
UmbracoContext.Current = umbracoContext;
return routingContext;
}
private void CreateHelper()
{
umbracoHelper = new UmbracoHelper(umbracoContext, currentPage);
}
private void InitializePublishedContentRequest()
{
// Some deep core methods fetch the published content request from routedata
// others access it through the context
// in any case, this is the one telling everyone which content is the current content.
publishedContentRequest = new PublishedContentRequest(new Uri(Url), routingContext, settings.WebRouting, s => new string[0])
{
PublishedContent = currentPage,
Culture = CultureInfo.CurrentCulture
};
umbracoContext.PublishedContentRequest = publishedContentRequest;
var routeDefinition = new RouteDefinition
{
PublishedContentRequest = publishedContentRequest
};
routeData.DataTokens.Add("umbraco-route-def", routeDefinition);
routeData.DataTokens.Add("umbraco-doc-request", publishedContentRequest);
}
private void SetupContentTypeService()
{
Mock.Get(ServiceContext.ContentTypeService).Setup(x => x.GetAllContentTypes(It.IsAny<int[]>()))
.Returns(contentTypes);
Mock.Get(ServiceContext.ContentTypeService).Setup(x => x.GetAllContentTypes(It.IsAny<IEnumerable<Guid>>()))
.Returns(contentTypes);
}
private int contentTypeId = 0;
public void SetupContentType(string alias, PropertyType[] propertyTypes, int id = 0)
{
PublishedContentType.GetPublishedContentTypeCallback = null;
var contentType = Mock.Of<IContentType>();
Mock.Get(this.ServiceContext.ContentTypeService).Setup(s => s.GetContentType(alias))
.Returns(contentType);
Mock.Get(this.ServiceContext.ContentTypeService).Setup(s => s.GetContentType(id))
.Returns(contentType);
var mock = Mock.Get(contentType);
mock.Setup(t => t.CompositionPropertyTypes).Returns(propertyTypes);
mock.Setup(t => t.Alias).Returns(alias);
mock.Setup(t => t.Icon).Returns("icon-for-" + alias);
mock.Setup(t => t.Id).Returns(id == 0 ? ++contentTypeId : id);
contentTypes.Add(contentType);
}
public void SetPublishedContentRequestDomain(IDomain domain)
{
publishedContentRequest.UmbracoDomain = domain;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment