From 480033f06a0221808fd522bc9c2b5b7ef6ab9f9e Mon Sep 17 00:00:00 2001 From: Greg White Date: Sun, 21 Aug 2022 10:56:39 +1200 Subject: [PATCH 01/10] Added the concept of named options profiles. --- src/Smidge.Core/Models/BundleExtensions.cs | 59 +++++++++++++++++-- .../Options/BundleEnvironmentOptions.cs | 58 ++++++++++++++---- .../Options/SmidgeOptionsProfile.cs | 11 ++++ src/Smidge/SmidgeHelper.cs | 22 ++++--- 4 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 src/Smidge.Core/Options/SmidgeOptionsProfile.cs diff --git a/src/Smidge.Core/Models/BundleExtensions.cs b/src/Smidge.Core/Models/BundleExtensions.cs index 7e3d4ad..ede6956 100644 --- a/src/Smidge.Core/Models/BundleExtensions.cs +++ b/src/Smidge.Core/Models/BundleExtensions.cs @@ -1,4 +1,5 @@ -using Smidge.Options; +using System; +using Smidge.Options; namespace Smidge.Models { @@ -11,14 +12,34 @@ public static class BundleExtensions /// /// /// + [Obsolete("Use GetBundleOptions(IBundleManager, string) and specify a configuration profile name.")] public static BundleOptions GetBundleOptions(this Bundle bundle, IBundleManager bundleMgr, bool debug) { var bundleOptions = debug - ? (bundle.BundleOptions == null ? bundleMgr.DefaultBundleOptions.DebugOptions : bundle.BundleOptions.DebugOptions) - : (bundle.BundleOptions == null ? bundleMgr.DefaultBundleOptions.ProductionOptions : bundle.BundleOptions.ProductionOptions); + ? GetBundleOptions(bundle, bundleMgr, SmidgeOptionsProfile.Debug) + : GetBundleOptions(bundle, bundleMgr, SmidgeOptionsProfile.Default); return bundleOptions; } + + /// + /// Get the bundle options from the bundle if they have been set otherwise with the defaults + /// + /// + /// + /// + /// + public static BundleOptions GetBundleOptions(this Bundle bundle, IBundleManager bundleMgr, string profileName) + { + var bundleOptions = bundle.BundleOptions == null + ? bundleMgr.DefaultBundleOptions[profileName] + : bundle.BundleOptions[profileName]; + + return bundleOptions; + } + + + /// /// Gets the default bundle options based on whether we're in debug or not @@ -26,6 +47,7 @@ public static BundleOptions GetBundleOptions(this Bundle bundle, IBundleManager /// /// /// + [Obsolete("Use GetDefaultBundleOptions(IBundleManager, string) and specify a configuration profile name.")] public static BundleOptions GetDefaultBundleOptions(this IBundleManager bundleMgr, bool debug) { var bundleOptions = debug @@ -35,11 +57,38 @@ public static BundleOptions GetDefaultBundleOptions(this IBundleManager bundleMg return bundleOptions; } + + /// + /// Gets the default bundle options for a particular configuration profile. + /// + /// + /// The name of a configuration profile. + /// + public static BundleOptions GetDefaultBundleOptions(this IBundleManager bundleMgr, string profileName) + { + var bundleOptions = bundleMgr.DefaultBundleOptions[profileName]; + + return bundleOptions; + } + + + + + + [Obsolete("Use GetAvailableOrDefaultBundleOptions(BundleOptions, string) and specify a configuration profile name.")] public static BundleOptions GetAvailableOrDefaultBundleOptions(this IBundleManager bundleMgr, BundleOptions options, bool debug) + { + return GetAvailableOrDefaultBundleOptions(bundleMgr, options, debug ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default); + } + + + public static BundleOptions GetAvailableOrDefaultBundleOptions(this IBundleManager bundleMgr, BundleOptions options, string profileName) { return options != null ? options - : bundleMgr.GetDefaultBundleOptions(debug); + : bundleMgr.GetDefaultBundleOptions(profileName); } + + } -} \ No newline at end of file +} diff --git a/src/Smidge.Core/Options/BundleEnvironmentOptions.cs b/src/Smidge.Core/Options/BundleEnvironmentOptions.cs index a184e41..f5ea4d5 100644 --- a/src/Smidge.Core/Options/BundleEnvironmentOptions.cs +++ b/src/Smidge.Core/Options/BundleEnvironmentOptions.cs @@ -1,12 +1,12 @@ -using System; -using Smidge.Cache; +using System.Collections.Concurrent; +using System.Collections.Generic; namespace Smidge.Options { /// - /// Defines the different bundle options for Debug vs Production + /// Defines the different bundle options for various configuration profiles such as Debug or Production /// public sealed class BundleEnvironmentOptions { @@ -20,14 +20,21 @@ public static BundleEnvironmentOptionsBuilder Create() return new BundleEnvironmentOptionsBuilder(options); } + + + private readonly IDictionary _profileOptions; + + /// /// Constructor, sets default options /// public BundleEnvironmentOptions() { + _profileOptions = new ConcurrentDictionary(); + DebugOptions = new BundleOptions { - + ProcessAsCompositeFile = false, CompressResult = false, CacheControlOptions = new CacheControlOptions @@ -36,17 +43,46 @@ public BundleEnvironmentOptions() CacheControlMaxAge = 0 } }; - ProductionOptions = new BundleOptions(); + ProductionOptions = new BundleOptions(); } - + /// - /// The options for debug mode + /// The options for the "debug" profile /// - public BundleOptions DebugOptions { get; set; } + public BundleOptions DebugOptions + { + get => this[SmidgeOptionsProfile.Debug]; + set => this[SmidgeOptionsProfile.Debug] = value; + } /// - /// The options for production mode + /// The options for "production" profile /// - public BundleOptions ProductionOptions { get; set; } + public BundleOptions ProductionOptions + { + get => this[SmidgeOptionsProfile.Production]; + set => this[SmidgeOptionsProfile.Production] = value; + } + + + + + public BundleOptions this[string profileName] + { + get + { + if (!_profileOptions.TryGetValue(profileName, out BundleOptions options)) + { + // Initialise a new BundleOptions for the requested profile + options = new BundleOptions(); + _profileOptions.Add(profileName, options); + } + + return options; + } + set => _profileOptions[profileName] = value; + } + + } -} \ No newline at end of file +} diff --git a/src/Smidge.Core/Options/SmidgeOptionsProfile.cs b/src/Smidge.Core/Options/SmidgeOptionsProfile.cs new file mode 100644 index 0000000..d1c2416 --- /dev/null +++ b/src/Smidge.Core/Options/SmidgeOptionsProfile.cs @@ -0,0 +1,11 @@ + +namespace Smidge.Options +{ + public static class SmidgeOptionsProfile + { + public const string Default = Production; + + public const string Debug = "Debug"; + public const string Production = "Production"; + } +} diff --git a/src/Smidge/SmidgeHelper.cs b/src/Smidge/SmidgeHelper.cs index 3cb54a8..43df415 100644 --- a/src/Smidge/SmidgeHelper.cs +++ b/src/Smidge/SmidgeHelper.cs @@ -1,4 +1,4 @@ -using Smidge.Models; +using Smidge.Models; using System; using System.Collections.Generic; using System.Linq; @@ -10,6 +10,7 @@ using Smidge.CompositeFiles; using Smidge.FileProcessors; using Smidge.Hashing; +using Smidge.Options; namespace Smidge { @@ -184,8 +185,10 @@ private IEnumerable GenerateBundleUrlsAsync(string bundleName, string fi var result = new List(); + var profileName = debug ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default; + //get the bundle options from the bundle if they have been set otherwise with the defaults - var bundleOptions = bundle.GetBundleOptions(_bundleManager, debug); + var bundleOptions = bundle.GetBundleOptions(_bundleManager, profileName); var cacheBuster = _cacheBusterResolver.GetCacheBuster(bundleOptions.GetCacheBusterType()); var cacheBusterValue = cacheBuster.GetValue(); @@ -197,11 +200,11 @@ private IEnumerable GenerateBundleUrlsAsync(string bundleName, string fi _processorFactory.CreateDefault( //the file type in the bundle will always be the same bundle.Files[0].DependencyType)); - result.AddRange(files.Select(d => _urlManager.AppendCacheBuster(_requestHelper.Content(d), debug, cacheBusterValue))); + result.AddRange(files.Select(d => _urlManager.AppendCacheBuster(_requestHelper.Content(d), !bundleOptions.ProcessAsCompositeFile, cacheBusterValue))); return result; } - var url = _urlManager.GetUrl(bundleName, fileExt, debug, cacheBusterValue); + var url = _urlManager.GetUrl(bundleName, fileExt, !bundleOptions.ProcessAsCompositeFile, cacheBusterValue); if (!string.IsNullOrWhiteSpace(url)) { result.Add(url); @@ -228,12 +231,15 @@ private async Task> GenerateUrlsAsync( var orderedFiles = _fileSetGenerator.GetOrderedFileSet(files, pipeline ?? _processorFactory.CreateDefault(fileType)); - var cacheBuster = _cacheBusterResolver.GetCacheBuster(_bundleManager.GetDefaultBundleOptions(debug).GetCacheBusterType()); + var profileName = debug ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default; + var bundleOptions = _bundleManager.GetDefaultBundleOptions(profileName); + + var cacheBuster = _cacheBusterResolver.GetCacheBuster(bundleOptions.GetCacheBusterType()); var cacheBusterValue = cacheBuster.GetValue(); - if (debug) + if (!bundleOptions.ProcessAsCompositeFile) { - return orderedFiles.Select(x => _urlManager.AppendCacheBuster(_requestHelper.Content(x), debug, cacheBusterValue)); + return orderedFiles.Select(x => _urlManager.AppendCacheBuster(_requestHelper.Content(x), true, cacheBusterValue)); } var compression = _requestHelper.GetClientCompression(_httpContextAccessor.HttpContext.Request.Headers); @@ -262,7 +268,7 @@ private async Task> GenerateUrlsAsync( { //now we need to determine if these files have already been minified - var defaultBundleOptions = _bundleManager.GetDefaultBundleOptions(false); + //var defaultBundleOptions = _bundleManager.GetDefaultBundleOptions(false); var cacheFile = _fileSystem.CacheFileSystem.GetCachedCompositeFile(cacheBusterValue, compression, u.Key, out _); if (!cacheFile.Exists) From 439a43c0ecf8c6b7e964fd71a6a0a949608e7da9 Mon Sep 17 00:00:00 2001 From: Greg White Date: Sun, 21 Aug 2022 13:00:01 +1200 Subject: [PATCH 02/10] Added ISmidgeProfileStrategy interface and implementations that determine which BundleOptions to use for a given request. --- src/Smidge.Core/DefaultProfileStrategy.cs | 14 +++++ .../FileProcessors/PreProcessManager.cs | 6 +-- .../HostEnvironmentProfileStrategy.cs | 33 ++++++++++++ src/Smidge.Core/ISmidgeProfileStrategy.cs | 12 +++++ src/Smidge.Core/Models/BundleExtensions.cs | 5 ++ src/Smidge/SmidgeHelper.cs | 53 +++++++++++++------ src/Smidge/SmidgeStartup.cs | 3 +- .../Helpers/FakeProfileStrategy.cs | 29 ++++++++++ test/Smidge.Tests/SmidgeHelperTests.cs | 13 +++-- 9 files changed, 144 insertions(+), 24 deletions(-) create mode 100644 src/Smidge.Core/DefaultProfileStrategy.cs create mode 100644 src/Smidge.Core/HostEnvironmentProfileStrategy.cs create mode 100644 src/Smidge.Core/ISmidgeProfileStrategy.cs create mode 100644 test/Smidge.Tests/Helpers/FakeProfileStrategy.cs diff --git a/src/Smidge.Core/DefaultProfileStrategy.cs b/src/Smidge.Core/DefaultProfileStrategy.cs new file mode 100644 index 0000000..64c3c7e --- /dev/null +++ b/src/Smidge.Core/DefaultProfileStrategy.cs @@ -0,0 +1,14 @@ +using Smidge.Options; + +namespace Smidge +{ + + /// + /// An implementation of ISmidgeProfileStrategy that will always use the Default profile. + /// + /// + public class DefaultProfileStrategy : ISmidgeProfileStrategy + { + public string GetCurrentProfileName() => SmidgeOptionsProfile.Default; + } +} diff --git a/src/Smidge.Core/FileProcessors/PreProcessManager.cs b/src/Smidge.Core/FileProcessors/PreProcessManager.cs index 181259f..7a607af 100644 --- a/src/Smidge.Core/FileProcessors/PreProcessManager.cs +++ b/src/Smidge.Core/FileProcessors/PreProcessManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.IO; using System.Threading; @@ -34,7 +34,7 @@ public async Task ProcessAndCacheFileAsync(IWebFile file, BundleOptions bundleOp if (file == null) throw new ArgumentNullException(nameof(file)); if (file.Pipeline == null) throw new ArgumentNullException($"{nameof(file)}.Pipeline"); - await ProcessFile(file, _bundleManager.GetAvailableOrDefaultBundleOptions(bundleOptions, false), bundleContext); + await ProcessFile(file, _bundleManager.GetAvailableOrDefaultBundleOptions(bundleOptions), bundleContext); } private async Task ProcessFile(IWebFile file, BundleOptions bundleOptions, BundleContext bundleContext) @@ -121,4 +121,4 @@ private static void FileModified(WatchedFile file) file.BundleOptions.FileWatchOptions.Changed(new FileWatchEventArgs(file)); } } -} \ No newline at end of file +} diff --git a/src/Smidge.Core/HostEnvironmentProfileStrategy.cs b/src/Smidge.Core/HostEnvironmentProfileStrategy.cs new file mode 100644 index 0000000..1fe14d5 --- /dev/null +++ b/src/Smidge.Core/HostEnvironmentProfileStrategy.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.Hosting; +using Smidge.Options; + +namespace Smidge +{ + /// + /// An implementation of ISmidgeProfileStrategy that will use the host environment to determine if the Debug profile should be used. + /// + /// + public class HostEnvironmentProfileStrategy : ISmidgeProfileStrategy + { + private readonly IHostEnvironment _hostEnvironment; + + + public HostEnvironmentProfileStrategy(IHostEnvironment hostEnvironment) + { + _hostEnvironment = hostEnvironment; + } + + + private string _profileName; + + public string GetCurrentProfileName() => _profileName ??= GetProfileForEnvironment(_hostEnvironment); + + + protected virtual string GetProfileForEnvironment(IHostEnvironment hostEnvironment) + { + return hostEnvironment.IsDevelopment() + ? SmidgeOptionsProfile.Debug + : SmidgeOptionsProfile.Default; + } + } +} diff --git a/src/Smidge.Core/ISmidgeProfileStrategy.cs b/src/Smidge.Core/ISmidgeProfileStrategy.cs new file mode 100644 index 0000000..0c010ea --- /dev/null +++ b/src/Smidge.Core/ISmidgeProfileStrategy.cs @@ -0,0 +1,12 @@ + +namespace Smidge +{ + + /// + /// An interface that returns the name of an options profile to use for the current request. + /// + public interface ISmidgeProfileStrategy + { + string GetCurrentProfileName(); + } +} diff --git a/src/Smidge.Core/Models/BundleExtensions.cs b/src/Smidge.Core/Models/BundleExtensions.cs index ede6956..cdf9efc 100644 --- a/src/Smidge.Core/Models/BundleExtensions.cs +++ b/src/Smidge.Core/Models/BundleExtensions.cs @@ -82,6 +82,11 @@ public static BundleOptions GetAvailableOrDefaultBundleOptions(this IBundleManag } + public static BundleOptions GetAvailableOrDefaultBundleOptions(this IBundleManager bundleMgr, BundleOptions options) + { + return GetAvailableOrDefaultBundleOptions(bundleMgr, options, SmidgeOptionsProfile.Default); + } + public static BundleOptions GetAvailableOrDefaultBundleOptions(this IBundleManager bundleMgr, BundleOptions options, string profileName) { return options != null diff --git a/src/Smidge/SmidgeHelper.cs b/src/Smidge/SmidgeHelper.cs index 43df415..d0a02c7 100644 --- a/src/Smidge/SmidgeHelper.cs +++ b/src/Smidge/SmidgeHelper.cs @@ -19,6 +19,7 @@ namespace Smidge /// public class SmidgeHelper : ISmidgeRequire { + private readonly ISmidgeProfileStrategy _profileStrategy; private readonly DynamicallyRegisteredWebFiles _dynamicallyRegisteredWebFiles; private readonly IPreProcessManager _preProcessManager; private readonly ISmidgeFileSystem _fileSystem; @@ -35,6 +36,7 @@ public class SmidgeHelper : ISmidgeRequire /// /// Constructor /// + /// /// /// /// @@ -47,6 +49,7 @@ public class SmidgeHelper : ISmidgeRequire /// /// public SmidgeHelper( + ISmidgeProfileStrategy profileStrategy, IBundleFileSetGenerator fileSetGenerator, DynamicallyRegisteredWebFiles dynamicallyRegisteredWebFiles, IPreProcessManager preProcessManager, @@ -59,6 +62,7 @@ public SmidgeHelper( IHttpContextAccessor httpContextAccessor, CacheBusterResolver cacheBusterResolver) { + _profileStrategy = profileStrategy ?? throw new ArgumentNullException(nameof(profileStrategy)); _fileSetGenerator = fileSetGenerator ?? throw new ArgumentNullException(nameof(fileSetGenerator)); _processorFactory = processorFactory ?? throw new ArgumentNullException(nameof(processorFactory)); _urlManager = urlManager ?? throw new ArgumentNullException(nameof(urlManager)); @@ -72,7 +76,7 @@ public SmidgeHelper( _fileBatcher = new FileBatcher(_fileSystem, _requestHelper, hasher); } - public async Task JsHereAsync(string bundleName, bool debug = false) + public async Task JsHereAsync(string bundleName, bool? debug = null) { var urls = await GenerateJsUrlsAsync(bundleName, debug); var result = new StringBuilder(); @@ -84,7 +88,7 @@ public async Task JsHereAsync(string bundleName, bool debug = false) return new HtmlString(result.ToString()); } - public async Task CssHereAsync(string bundleName, bool debug = false) + public async Task CssHereAsync(string bundleName, bool? debug = null) { var urls = await GenerateCssUrlsAsync(bundleName, debug); var result = new StringBuilder(); @@ -104,7 +108,7 @@ public async Task CssHereAsync(string bundleName, bool debug = false /// TODO: Once the tags are rendered the collection on the context is cleared. Therefore if this method is called multiple times it will /// render anything that has been registered as 'pending' but has not been rendered. /// - public async Task JsHereAsync(PreProcessPipeline pipeline = null, bool debug = false) + public async Task JsHereAsync(PreProcessPipeline pipeline = null, bool? debug = null) { var result = new StringBuilder(); var urls = await GenerateJsUrlsAsync(pipeline, debug); @@ -123,7 +127,7 @@ public async Task JsHereAsync(PreProcessPipeline pipeline = null, bo /// TODO: Once the tags are rendered the collection on the context is cleared. Therefore if this method is called multiple times it will /// render anything that has been registered as 'pending' but has not been rendered. /// - public async Task CssHereAsync(PreProcessPipeline pipeline = null, bool debug = false) + public async Task CssHereAsync(PreProcessPipeline pipeline = null, bool? debug = null) { var result = new StringBuilder(); var urls = await GenerateCssUrlsAsync(pipeline, debug); @@ -138,12 +142,12 @@ public async Task CssHereAsync(PreProcessPipeline pipeline = null, b /// Generates the list of URLs to render based on what is dynamically registered /// /// - public async Task> GenerateJsUrlsAsync(PreProcessPipeline pipeline = null, bool debug = false) + public async Task> GenerateJsUrlsAsync(PreProcessPipeline pipeline = null, bool? debug = null) { return await GenerateUrlsAsync(_dynamicallyRegisteredWebFiles.JavaScriptFiles, WebFileType.Js, pipeline, debug); } - public Task> GenerateJsUrlsAsync(string bundleName, bool debug = false) + public Task> GenerateJsUrlsAsync(string bundleName, bool? debug = null) { return Task.FromResult(GenerateBundleUrlsAsync(bundleName, ".js", debug)); } @@ -152,12 +156,12 @@ public Task> GenerateJsUrlsAsync(string bundleName, bool deb /// Generates the list of URLs to render based on what is dynamically registered /// /// - public async Task> GenerateCssUrlsAsync(PreProcessPipeline pipeline = null, bool debug = false) + public async Task> GenerateCssUrlsAsync(PreProcessPipeline pipeline = null, bool? debug = null) { return await GenerateUrlsAsync(_dynamicallyRegisteredWebFiles.CssFiles, WebFileType.Css, pipeline, debug); } - public Task> GenerateCssUrlsAsync(string bundleName, bool debug = false) + public Task> GenerateCssUrlsAsync(string bundleName, bool? debug = null) { return Task.FromResult(GenerateBundleUrlsAsync(bundleName, ".css", debug)); } @@ -169,7 +173,7 @@ public Task> GenerateCssUrlsAsync(string bundleName, bool de /// /// /// - private IEnumerable GenerateBundleUrlsAsync(string bundleName, string fileExt, bool debug) + private IEnumerable GenerateBundleUrlsAsync(string bundleName, string fileExt, bool? debug = null) { //TODO: We should cache this, but problem is how do we do that with file watchers enabled? We'd still have to lookup the bundleOptions // or maybe we just cache when file watchers are not enabled - probably the way to do it @@ -185,7 +189,16 @@ private IEnumerable GenerateBundleUrlsAsync(string bundleName, string fi var result = new List(); - var profileName = debug ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default; + string profileName; + if (debug != null) + { + // Backwards compatibility - use the Debug parameter to choose the profile to use + profileName = debug.Value ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default; + } + else + { + profileName = _profileStrategy.GetCurrentProfileName(); + } //get the bundle options from the bundle if they have been set otherwise with the defaults var bundleOptions = bundle.GetBundleOptions(_bundleManager, profileName); @@ -221,17 +234,23 @@ private IEnumerable GenerateBundleUrlsAsync(string bundleName, string fi /// /// /// - private async Task> GenerateUrlsAsync( - IEnumerable files, - WebFileType fileType, - PreProcessPipeline pipeline = null, - bool debug = false) + private async Task> GenerateUrlsAsync(IEnumerable files, WebFileType fileType, PreProcessPipeline pipeline = null, bool? debug = null) { var result = new List(); var orderedFiles = _fileSetGenerator.GetOrderedFileSet(files, pipeline ?? _processorFactory.CreateDefault(fileType)); - var profileName = debug ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default; + string profileName; + if (debug != null) + { + // Backwards compatibility - use the Debug parameter to choose the profile to use + profileName = debug.Value ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default; + } + else + { + profileName = _profileStrategy.GetCurrentProfileName(); + } + var bundleOptions = _bundleManager.GetDefaultBundleOptions(profileName); var cacheBuster = _cacheBusterResolver.GetCacheBuster(bundleOptions.GetCacheBusterType()); @@ -278,7 +297,7 @@ private async Task> GenerateUrlsAsync( //need to process/minify these files - need to use their original paths of course foreach (var file in batch.Select(x => x.Original)) { - await _preProcessManager.ProcessAndCacheFileAsync(file, null, bundleContext); + await _preProcessManager.ProcessAndCacheFileAsync(file, bundleOptions, bundleContext); } } } diff --git a/src/Smidge/SmidgeStartup.cs b/src/Smidge/SmidgeStartup.cs index 81249e9..3ba8ca8 100644 --- a/src/Smidge/SmidgeStartup.cs +++ b/src/Smidge/SmidgeStartup.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Infrastructure; @@ -34,6 +34,7 @@ public static IServiceCollection AddSmidge(this IServiceCollection services, ICo services.TryAddSingleton(); services.TryAddSingleton(); + services.AddSingleton(); services.AddTransient, SmidgeOptionsSetup>(); services.AddSingleton(); diff --git a/test/Smidge.Tests/Helpers/FakeProfileStrategy.cs b/test/Smidge.Tests/Helpers/FakeProfileStrategy.cs new file mode 100644 index 0000000..36fb9c3 --- /dev/null +++ b/test/Smidge.Tests/Helpers/FakeProfileStrategy.cs @@ -0,0 +1,29 @@ +using Smidge.Options; + +namespace Smidge.Tests.Helpers +{ + public class FakeProfileStrategy : ISmidgeProfileStrategy + { + public static readonly ISmidgeProfileStrategy DebugProfileStrategy = new FakeProfileStrategy(SmidgeOptionsProfile.Debug); + public static readonly ISmidgeProfileStrategy DefaultProfileStrategy = new FakeProfileStrategy(SmidgeOptionsProfile.Default); + + + public FakeProfileStrategy() + { + ProfileName = SmidgeOptionsProfile.Default; + } + + public FakeProfileStrategy(string profileName) + { + ProfileName = profileName; + } + + + public string ProfileName { get; set; } + + + public string GetCurrentProfileName() => ProfileName; + + + } +} diff --git a/test/Smidge.Tests/SmidgeHelperTests.cs b/test/Smidge.Tests/SmidgeHelperTests.cs index 71ff88d..d84ada8 100644 --- a/test/Smidge.Tests/SmidgeHelperTests.cs +++ b/test/Smidge.Tests/SmidgeHelperTests.cs @@ -14,6 +14,7 @@ using Smidge.Hashing; using Smidge.FileProcessors; using Smidge.Options; +using Smidge.Tests.Helpers; using Xunit; namespace Smidge.Tests @@ -47,6 +48,7 @@ public SmidgeHelperTests() _dynamicallyRegisteredWebFiles = new DynamicallyRegisteredWebFiles(); _fileSystemHelper = new SmidgeFileSystem(_fileProvider, _fileProviderFilter, _cacheProvider, Mock.Of()); + _smidgeOptions = new Mock>(); _smidgeOptions.Setup(opt => opt.Value).Returns(new SmidgeOptions { @@ -69,6 +71,7 @@ public SmidgeHelperTests() public async Task JsHereAsync_Returns_Empty_String_Result_When_No_Files_Found() { var sut = new SmidgeHelper( + FakeProfileStrategy.DefaultProfileStrategy, _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, @@ -77,7 +80,7 @@ public async Task JsHereAsync_Returns_Empty_String_Result_When_No_Files_Found() _bundleManager.CreateJs("empty", Array.Empty()); - var result = (await sut.JsHereAsync("empty", false)).ToString(); + var result = (await sut.JsHereAsync("empty")).ToString(); Assert.Equal(string.Empty, result); } @@ -85,6 +88,7 @@ public async Task JsHereAsync_Returns_Empty_String_Result_When_No_Files_Found() public async Task Generate_Css_Urls_For_Non_Existent_Bundle_Throws_Exception() { var sut = new SmidgeHelper( + FakeProfileStrategy.DebugProfileStrategy, _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, @@ -93,7 +97,7 @@ public async Task Generate_Css_Urls_For_Non_Existent_Bundle_Throws_Exception() var exception = await Assert.ThrowsAsync ( - async () => await sut.GenerateCssUrlsAsync("DoesntExist", true) + async () => await sut.GenerateCssUrlsAsync("DoesntExist") ); @@ -105,6 +109,7 @@ public async Task Generate_Js_Urls_For_Non_Existent_Bundle_Throws_Exception() { var sut = new SmidgeHelper( + FakeProfileStrategy.DebugProfileStrategy, _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, @@ -113,7 +118,7 @@ public async Task Generate_Js_Urls_For_Non_Existent_Bundle_Throws_Exception() var exception = await Assert.ThrowsAsync ( - async () => await sut.GenerateJsUrlsAsync("DoesntExist", true) + async () => await sut.GenerateJsUrlsAsync("DoesntExist") ); @@ -124,6 +129,7 @@ public async Task CssHere_HtmlString_For_Non_Existent_Css_Bundle_Throws_Exceptio { var sut = new SmidgeHelper( + FakeProfileStrategy.DebugProfileStrategy, _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, @@ -148,6 +154,7 @@ public async Task JsHere_HtmlString_For_Non_Existent_Css_Bundle_Throws_Exception { var sut = new SmidgeHelper( + FakeProfileStrategy.DebugProfileStrategy, _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, From b1764f2c870a1ff9e01914a1cf8b2d3ab6a45055 Mon Sep 17 00:00:00 2001 From: Greg White Date: Sun, 21 Aug 2022 13:01:48 +1200 Subject: [PATCH 03/10] Updated TagHelpers to pass a null Debug parameter to SmidgeHelper by default so the ISmidgeProfileStrategy is used to determine which profile to use. The Debug parameter can still be used to override the output on a case-by-case basis. --- src/Smidge/TagHelpers/SmidgeLinkTagHelper.cs | 6 +++++- src/Smidge/TagHelpers/SmidgeScriptTagHelper.cs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Smidge/TagHelpers/SmidgeLinkTagHelper.cs b/src/Smidge/TagHelpers/SmidgeLinkTagHelper.cs index 239031a..29d3792 100644 --- a/src/Smidge/TagHelpers/SmidgeLinkTagHelper.cs +++ b/src/Smidge/TagHelpers/SmidgeLinkTagHelper.cs @@ -61,8 +61,12 @@ public SmidgeLinkTagHelper(SmidgeHelper smidgeHelper, IBundleManager bundleManag [HtmlAttributeName(HrefAttributeName)] public string Source { get; set; } + /// + /// Gets or sets a value indicating whether to generate content based on the debug or production configuration profile. + /// If left unset then the configured will determine if the debug profile is used. + /// [HtmlAttributeName("debug")] - public bool Debug { get; set; } + public bool? Debug { get; set; } public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { diff --git a/src/Smidge/TagHelpers/SmidgeScriptTagHelper.cs b/src/Smidge/TagHelpers/SmidgeScriptTagHelper.cs index 75ed561..efb6beb 100644 --- a/src/Smidge/TagHelpers/SmidgeScriptTagHelper.cs +++ b/src/Smidge/TagHelpers/SmidgeScriptTagHelper.cs @@ -59,8 +59,12 @@ public SmidgeScriptTagHelper(SmidgeHelper smidgeHelper, IBundleManager bundleMan [HtmlAttributeName("src")] public string Source { get; set; } + /// + /// Gets or sets a value indicating whether to generate content based on the debug or production configuration profile. + /// If left unset then the configured will determine if the debug profile is used. + /// [HtmlAttributeName("debug")] - public bool Debug { get; set; } + public bool? Debug { get; set; } public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { From 03e3fafd9410e243192ecb3bf1191bd50fda2be3 Mon Sep 17 00:00:00 2001 From: Greg White Date: Sun, 21 Aug 2022 13:03:15 +1200 Subject: [PATCH 04/10] Updated the test website to not specify the Debug parameter, forcing the use of the default configured ISmidgeProfileStrategy. --- src/Smidge.Web/Views/Home/Index.cshtml | 6 +++--- src/Smidge.Web/Views/Home/SubFolder.cshtml | 4 ++-- src/Smidge.Web/Views/Shared/LoadedDependencies.cshtml | 6 ++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Smidge.Web/Views/Home/Index.cshtml b/src/Smidge.Web/Views/Home/Index.cshtml index 393d56b..4def666 100644 --- a/src/Smidge.Web/Views/Home/Index.cshtml +++ b/src/Smidge.Web/Views/Home/Index.cshtml @@ -38,12 +38,12 @@ @await Html.PartialAsync("TopBar") - @await Html.PartialAsync("LoadedDependencies", false) + @await Html.PartialAsync("LoadedDependencies", (bool?)null) - @await SmidgeHelper.JsHereAsync(debug: false) + @await SmidgeHelper.JsHereAsync() @await SmidgeHelper.JsHereAsync("test-bundle-1") @await SmidgeHelper.JsHereAsync("test-bundle-2") - @await SmidgeHelper.JsHereAsync("no-files", debug: false) + @await SmidgeHelper.JsHereAsync("no-files") @await SmidgeHelper.JsHereAsync("test-bundle-10") diff --git a/src/Smidge.Web/Views/Home/SubFolder.cshtml b/src/Smidge.Web/Views/Home/SubFolder.cshtml index fef9db5..97ef221 100644 --- a/src/Smidge.Web/Views/Home/SubFolder.cshtml +++ b/src/Smidge.Web/Views/Home/SubFolder.cshtml @@ -32,9 +32,9 @@ @await Html.PartialAsync("TopBar") - @await Html.PartialAsync("LoadedDependencies", false) + @await Html.PartialAsync("LoadedDependencies", (bool?)null) - @await SmidgeHelper.JsHereAsync(debug: false) + @await SmidgeHelper.JsHereAsync() @await SmidgeHelper.JsHereAsync("test-bundle-1") @await SmidgeHelper.JsHereAsync("test-bundle-2") diff --git a/src/Smidge.Web/Views/Shared/LoadedDependencies.cshtml b/src/Smidge.Web/Views/Shared/LoadedDependencies.cshtml index 21f36c1..77241e3 100644 --- a/src/Smidge.Web/Views/Shared/LoadedDependencies.cshtml +++ b/src/Smidge.Web/Views/Shared/LoadedDependencies.cshtml @@ -1,5 +1,7 @@ -@model bool +@model bool? + @using Smidge.Models; + @inject Smidge.SmidgeHelper SmidgeHelper @inject Smidge.IBundleManager BundleManager @@ -43,4 +45,4 @@
JS loading debug output: -
\ No newline at end of file + From 60c813781530475ad175991c2d97eef85e6cc784 Mon Sep 17 00:00:00 2001 From: Greg White Date: Sun, 21 Aug 2022 15:25:12 +1200 Subject: [PATCH 05/10] Added some unit tests to test the url generation based on the profile being used. --- .../PreProcessPipelineFactory.cs | 19 +- test/Smidge.Tests/Helpers/FakeCacheBuster.cs | 14 + test/Smidge.Tests/Helpers/FakeWebsiteInfo.cs | 13 + test/Smidge.Tests/SmidgeHelperTests.cs | 257 +++++++++++++++--- 4 files changed, 263 insertions(+), 40 deletions(-) create mode 100644 test/Smidge.Tests/Helpers/FakeCacheBuster.cs create mode 100644 test/Smidge.Tests/Helpers/FakeWebsiteInfo.cs diff --git a/src/Smidge.Core/FileProcessors/PreProcessPipelineFactory.cs b/src/Smidge.Core/FileProcessors/PreProcessPipelineFactory.cs index 9698afd..4455ea3 100644 --- a/src/Smidge.Core/FileProcessors/PreProcessPipelineFactory.cs +++ b/src/Smidge.Core/FileProcessors/PreProcessPipelineFactory.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Smidge.Models; using System.Linq; @@ -67,19 +67,20 @@ private PreProcessPipeline GetDefault(WebFileType fileType) switch (fileType) { case WebFileType.Js: + return new PreProcessPipeline(new IPreProcessor[] { - _allProcessors.Value.OfType().First(), - _allProcessors.Value.OfType().First() - }); + _allProcessors.Value.OfType().FirstOrDefault(), + _allProcessors.Value.OfType().FirstOrDefault() + }.Where(p => p != null)); case WebFileType.Css: default: return new PreProcessPipeline(new IPreProcessor[] { - _allProcessors.Value.OfType().First(), - _allProcessors.Value.OfType().First(), - _allProcessors.Value.OfType().First() - }); + _allProcessors.Value.OfType().FirstOrDefault(), + _allProcessors.Value.OfType().FirstOrDefault(), + _allProcessors.Value.OfType().FirstOrDefault() + }.Where(p => p != null)); } }); } @@ -93,4 +94,4 @@ public Func OnCreateDefault set { _setGetDefaultCallback = value; } } } -} \ No newline at end of file +} diff --git a/test/Smidge.Tests/Helpers/FakeCacheBuster.cs b/test/Smidge.Tests/Helpers/FakeCacheBuster.cs new file mode 100644 index 0000000..02aee3f --- /dev/null +++ b/test/Smidge.Tests/Helpers/FakeCacheBuster.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Smidge.Cache; + +namespace Smidge.Tests.Helpers +{ + public class FakeCacheBuster : ICacheBuster + { + + public static readonly IEnumerable Instances = new[] { new FakeCacheBuster() }; + + + public string GetValue() => "00000"; + } +} diff --git a/test/Smidge.Tests/Helpers/FakeWebsiteInfo.cs b/test/Smidge.Tests/Helpers/FakeWebsiteInfo.cs new file mode 100644 index 0000000..22052f7 --- /dev/null +++ b/test/Smidge.Tests/Helpers/FakeWebsiteInfo.cs @@ -0,0 +1,13 @@ +using System; + +namespace Smidge.Tests.Helpers +{ + public class FakeWebsiteInfo : IWebsiteInfo + { + + private Uri _baseUrl; + public Uri GetBaseUrl() => _baseUrl ??= new Uri("http://test.com"); + + public string GetBasePath() => string.Empty; + } +} diff --git a/test/Smidge.Tests/SmidgeHelperTests.cs b/test/Smidge.Tests/SmidgeHelperTests.cs index d84ada8..87a5d65 100644 --- a/test/Smidge.Tests/SmidgeHelperTests.cs +++ b/test/Smidge.Tests/SmidgeHelperTests.cs @@ -1,15 +1,16 @@ using System; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Moq; using Smidge.CompositeFiles; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Dazinator.Extensions.FileProviders; +using Dazinator.Extensions.FileProviders.InMemory; +using Dazinator.Extensions.FileProviders.InMemory.Directory; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Smidge; using Smidge.Cache; using Smidge.Hashing; using Smidge.FileProcessors; @@ -21,10 +22,9 @@ namespace Smidge.Tests { public class SmidgeHelperTests { - private readonly IUrlManager _urlManager = Mock.Of(); + private readonly IUrlManager _urlManager;// = Mock.Of(); private readonly IFileProvider _fileProvider = Mock.Of(); private readonly ICacheFileSystem _cacheProvider = Mock.Of(); - private readonly IFileProviderFilter _fileProviderFilter = Mock.Of(); private readonly IHasher _hasher = Mock.Of(); private readonly IEnumerable _preProcessors = new List(); private readonly IBundleFileSetGenerator _fileSetGenerator; @@ -35,35 +35,44 @@ public class SmidgeHelperTests private readonly PreProcessPipelineFactory _processorFactory; private readonly IBundleManager _bundleManager; private readonly IRequestHelper _requestHelper; + private readonly CacheBusterResolver _cacheBusterResolver; private readonly Mock _httpContextAccessor; - private Mock _httpContext; public SmidgeHelperTests() { - // var config = Mock.Of(); - _httpContext = new Mock(); _httpContextAccessor = new Mock(); - _httpContextAccessor.Setup(x => x.HttpContext).Returns(_httpContext.Object); + _httpContextAccessor.Setup(x => x.HttpContext).Returns(Mock.Of); _dynamicallyRegisteredWebFiles = new DynamicallyRegisteredWebFiles(); - _fileSystemHelper = new SmidgeFileSystem(_fileProvider, _fileProviderFilter, _cacheProvider, Mock.Of()); + _fileSystemHelper = new SmidgeFileSystem(_fileProvider, new DefaultFileProviderFilter(), _cacheProvider, new FakeWebsiteInfo()); _smidgeOptions = new Mock>(); - _smidgeOptions.Setup(opt => opt.Value).Returns(new SmidgeOptions + _smidgeOptions.Setup(opt => opt.Value).Returns(() => { - DefaultBundleOptions = new BundleEnvironmentOptions() + var options = new SmidgeOptions + { + UrlOptions = new UrlManagerOptions(), + DefaultBundleOptions = new BundleEnvironmentOptions() + }; + options.DefaultBundleOptions.DebugOptions.SetCacheBusterType(); + options.DefaultBundleOptions.ProductionOptions.SetCacheBusterType(); + return options; }); - _requestHelper = Mock.Of(); + _requestHelper = new RequestHelper(new FakeWebsiteInfo()); + _urlManager = new DefaultUrlManager(_smidgeOptions.Object, _hasher, _requestHelper); + + _cacheBusterResolver = new CacheBusterResolver(FakeCacheBuster.Instances); + _processorFactory = new PreProcessPipelineFactory(new Lazy>(() => _preProcessors)); _bundleManager = new BundleManager(_smidgeOptions.Object, Mock.Of>()); _preProcessManager = new PreProcessManager( - _fileSystemHelper, - _bundleManager, + _fileSystemHelper, + _bundleManager, Mock.Of>()); _fileSetGenerator = new BundleFileSetGenerator( - _fileSystemHelper, + _fileSystemHelper, new FileProcessingConventions(_smidgeOptions.Object, new List())); } @@ -73,10 +82,9 @@ public async Task JsHereAsync_Returns_Empty_String_Result_When_No_Files_Found() var sut = new SmidgeHelper( FakeProfileStrategy.DefaultProfileStrategy, _fileSetGenerator, - _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, + _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, - new CacheBusterResolver(Enumerable.Empty())); + _httpContextAccessor.Object, _cacheBusterResolver); _bundleManager.CreateJs("empty", Array.Empty()); @@ -90,10 +98,9 @@ public async Task Generate_Css_Urls_For_Non_Existent_Bundle_Throws_Exception() var sut = new SmidgeHelper( FakeProfileStrategy.DebugProfileStrategy, _fileSetGenerator, - _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, - _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, - new CacheBusterResolver(Enumerable.Empty())); + _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, + _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, + _httpContextAccessor.Object, _cacheBusterResolver); var exception = await Assert.ThrowsAsync ( @@ -104,6 +111,101 @@ public async Task Generate_Css_Urls_For_Non_Existent_Bundle_Throws_Exception() } + + [Fact] + public async Task Generate_Css_Urls_Returns_SingleBundleUrl_When_Default_Profile_Is_Used() + { + var sut = new SmidgeHelper( + FakeProfileStrategy.DefaultProfileStrategy, + _fileSetGenerator, + _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, + _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, + _httpContextAccessor.Object, _cacheBusterResolver); + + _bundleManager.CreateCss("test", new[] + { + "file1.css", + "file2.css" + }); + + var dir = new InMemoryDirectory(); + dir.AddFile("", new StringFileInfo("File1", "file1.css")); + dir.AddFile("", new StringFileInfo("File2", "file2.css")); + var fileProvider = new InMemoryFileProvider(dir); + + // Configure the mock file provider to use the temporary file provider we've just configured + Mock.Get(_fileProvider).Setup(f => f.GetFileInfo(It.IsAny())).Returns((string s) => fileProvider.GetFileInfo(s)); + Mock.Get(_fileProvider).Setup(f => f.GetDirectoryContents(It.IsAny())).Returns((string s) => fileProvider.GetDirectoryContents(s)); + + var urls = await sut.GenerateCssUrlsAsync("test"); + + Assert.Equal("/sb/test.css.v00000", urls.FirstOrDefault()); + } + + + [Fact] + public async Task Generate_Css_Urls_Returns_Multiple_Urls_When_Debug_Profile_Is_Used() + { + var sut = new SmidgeHelper( + FakeProfileStrategy.DebugProfileStrategy, + _fileSetGenerator, + _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, + _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, + _httpContextAccessor.Object, _cacheBusterResolver); + + _bundleManager.CreateCss("test", new[] + { + "file1.css", + "file2.css" + }); + + var dir = new InMemoryDirectory(); + dir.AddFile("", new StringFileInfo("File1", "file1.css")); + dir.AddFile("", new StringFileInfo("File2", "file2.css")); + var fileProvider = new InMemoryFileProvider(dir); + + // Configure the mock file provider to use the temporary file provider we've just configured + Mock.Get(_fileProvider).Setup(f => f.GetFileInfo(It.IsAny())).Returns((string s) => fileProvider.GetFileInfo(s)); + Mock.Get(_fileProvider).Setup(f => f.GetDirectoryContents(It.IsAny())).Returns((string s) => fileProvider.GetDirectoryContents(s)); + + var urls = await sut.GenerateJsUrlsAsync("test"); + + Assert.Equal("/file1.css?d=00000", urls.ElementAtOrDefault(0)); + Assert.Equal("/file2.css?d=00000", urls.ElementAtOrDefault(1)); + } + + [Fact] + public async Task Generate_Css_Urls_Returns_Multiple_Urls_When_Debug_Parameter_Overrides_Profile() + { + var sut = new SmidgeHelper( + FakeProfileStrategy.DefaultProfileStrategy, + _fileSetGenerator, + _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, + _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, + _httpContextAccessor.Object, _cacheBusterResolver); + + _bundleManager.CreateCss("test", new[] + { + "file1.css", + "file2.css" + }); + + var dir = new InMemoryDirectory(); + dir.AddFile("", new StringFileInfo("File1", "file1.css")); + dir.AddFile("", new StringFileInfo("File2", "file2.css")); + var fileProvider = new InMemoryFileProvider(dir); + + // Configure the mock file provider to use the temporary file provider we've just configured + Mock.Get(_fileProvider).Setup(f => f.GetFileInfo(It.IsAny())).Returns((string s) => fileProvider.GetFileInfo(s)); + Mock.Get(_fileProvider).Setup(f => f.GetDirectoryContents(It.IsAny())).Returns((string s) => fileProvider.GetDirectoryContents(s)); + + var urls = await sut.GenerateJsUrlsAsync("test", debug: true); + + Assert.Equal("/file1.css?d=00000", urls.ElementAtOrDefault(0)); + Assert.Equal("/file2.css?d=00000", urls.ElementAtOrDefault(1)); + } + + [Fact] public async Task Generate_Js_Urls_For_Non_Existent_Bundle_Throws_Exception() { @@ -111,19 +213,114 @@ public async Task Generate_Js_Urls_For_Non_Existent_Bundle_Throws_Exception() var sut = new SmidgeHelper( FakeProfileStrategy.DebugProfileStrategy, _fileSetGenerator, - _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, + _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, - new CacheBusterResolver(Enumerable.Empty())); + _httpContextAccessor.Object, _cacheBusterResolver); var exception = await Assert.ThrowsAsync ( async () => await sut.GenerateJsUrlsAsync("DoesntExist") ); + } + + + + [Fact] + public async Task Generate_Js_Urls_Returns_SingleBundleUrl_When_Default_Profile_Is_Used() + { + var sut = new SmidgeHelper( + FakeProfileStrategy.DefaultProfileStrategy, + _fileSetGenerator, + _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, + _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, + _httpContextAccessor.Object, _cacheBusterResolver); + + _bundleManager.CreateJs("test", new[] + { + "file1.js", + "file2.js" + }); + var dir = new InMemoryDirectory(); + dir.AddFile("", new StringFileInfo("File1", "file1.js")); + dir.AddFile("", new StringFileInfo("File2", "file2.js")); + var fileProvider = new InMemoryFileProvider(dir); + + // Configure the mock file provider to use the temporary file provider we've just configured + Mock.Get(_fileProvider).Setup(f => f.GetFileInfo(It.IsAny())).Returns((string s) => fileProvider.GetFileInfo(s)); + Mock.Get(_fileProvider).Setup(f => f.GetDirectoryContents(It.IsAny())).Returns((string s) => fileProvider.GetDirectoryContents(s)); + + var urls = await sut.GenerateJsUrlsAsync("test"); + + Assert.Equal("/sb/test.js.v00000", urls.FirstOrDefault()); } + + [Fact] + public async Task Generate_Js_Urls_Returns_Multiple_Urls_When_Debug_Profile_Is_Used() + { + var sut = new SmidgeHelper( + FakeProfileStrategy.DebugProfileStrategy, + _fileSetGenerator, + _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, + _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, + _httpContextAccessor.Object, _cacheBusterResolver); + + _bundleManager.CreateJs("test", new[] + { + "file1.js", + "file2.js" + }); + + var dir = new InMemoryDirectory(); + dir.AddFile("", new StringFileInfo("File1", "file1.js")); + dir.AddFile("", new StringFileInfo("File2", "file2.js")); + var fileProvider = new InMemoryFileProvider(dir); + + // Configure the mock file provider to use the temporary file provider we've just configured + Mock.Get(_fileProvider).Setup(f => f.GetFileInfo(It.IsAny())).Returns((string s) => fileProvider.GetFileInfo(s)); + Mock.Get(_fileProvider).Setup(f => f.GetDirectoryContents(It.IsAny())).Returns((string s) => fileProvider.GetDirectoryContents(s)); + + var urls = await sut.GenerateJsUrlsAsync("test"); + + Assert.Equal("/file1.js?d=00000", urls.ElementAtOrDefault(0)); + Assert.Equal("/file2.js?d=00000", urls.ElementAtOrDefault(1)); + } + + [Fact] + public async Task Generate_Js_Urls_Returns_Multiple_Urls_When_Debug_Parameter_Overrides_Profile() + { + var sut = new SmidgeHelper( + FakeProfileStrategy.DefaultProfileStrategy, + _fileSetGenerator, + _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, + _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, + _httpContextAccessor.Object, _cacheBusterResolver); + + _bundleManager.CreateJs("test", new[] + { + "file1.js", + "file2.js" + }); + + var dir = new InMemoryDirectory(); + dir.AddFile("", new StringFileInfo("File1", "file1.js")); + dir.AddFile("", new StringFileInfo("File2", "file2.js")); + var fileProvider = new InMemoryFileProvider(dir); + + // Configure the mock file provider to use the temporary file provider we've just configured + Mock.Get(_fileProvider).Setup(f => f.GetFileInfo(It.IsAny())).Returns((string s) => fileProvider.GetFileInfo(s)); + Mock.Get(_fileProvider).Setup(f => f.GetDirectoryContents(It.IsAny())).Returns((string s) => fileProvider.GetDirectoryContents(s)); + + var urls = await sut.GenerateJsUrlsAsync("test", debug: true); + + Assert.Equal("/file1.js?d=00000", urls.ElementAtOrDefault(0)); + Assert.Equal("/file2.js?d=00000", urls.ElementAtOrDefault(1)); + } + + + [Fact] public async Task CssHere_HtmlString_For_Non_Existent_Css_Bundle_Throws_Exception() { @@ -131,10 +328,9 @@ public async Task CssHere_HtmlString_For_Non_Existent_Css_Bundle_Throws_Exceptio var sut = new SmidgeHelper( FakeProfileStrategy.DebugProfileStrategy, _fileSetGenerator, - _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, + _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, - new CacheBusterResolver(Enumerable.Empty())); + _httpContextAccessor.Object, _cacheBusterResolver); var exception = await Assert.ThrowsAsync ( @@ -156,10 +352,9 @@ public async Task JsHere_HtmlString_For_Non_Existent_Css_Bundle_Throws_Exception var sut = new SmidgeHelper( FakeProfileStrategy.DebugProfileStrategy, _fileSetGenerator, - _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, + _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, - new CacheBusterResolver(Enumerable.Empty())); + _httpContextAccessor.Object, _cacheBusterResolver); var exception = await Assert.ThrowsAsync ( From 7b1dfec15043b8f2f25acf50f443d2fe570f3211 Mon Sep 17 00:00:00 2001 From: Greg White Date: Sun, 21 Aug 2022 16:04:46 +1200 Subject: [PATCH 06/10] Added ForProfile() method to allow configuration of named profile options. --- .../Options/BundleEnvironmentOptions.cs | 6 ++- .../BundleEnvironmentOptionsBuilder.cs | 42 +++++++++++-------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/Smidge.Core/Options/BundleEnvironmentOptions.cs b/src/Smidge.Core/Options/BundleEnvironmentOptions.cs index f5ea4d5..40c360d 100644 --- a/src/Smidge.Core/Options/BundleEnvironmentOptions.cs +++ b/src/Smidge.Core/Options/BundleEnvironmentOptions.cs @@ -66,7 +66,11 @@ public BundleOptions ProductionOptions - + /// + /// Gets or sets the for the specified profile. + /// + /// Name of the profile. + /// public BundleOptions this[string profileName] { get diff --git a/src/Smidge.Core/Options/BundleEnvironmentOptionsBuilder.cs b/src/Smidge.Core/Options/BundleEnvironmentOptionsBuilder.cs index 9fa4a32..1f3e27f 100644 --- a/src/Smidge.Core/Options/BundleEnvironmentOptionsBuilder.cs +++ b/src/Smidge.Core/Options/BundleEnvironmentOptionsBuilder.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Collections.Generic; namespace Smidge.Options { @@ -8,29 +9,39 @@ namespace Smidge.Options public sealed class BundleEnvironmentOptionsBuilder { private readonly BundleEnvironmentOptions _bundleEnvironmentOptions; - private Action _debugBuilder; - private Action _productionBuilder; - private bool _built = false; + + private readonly Dictionary> _profileBuilders; + private readonly bool _built = false; public BundleEnvironmentOptionsBuilder(BundleEnvironmentOptions bundleEnvironmentOptions) { + _profileBuilders = new Dictionary>(); + _bundleEnvironmentOptions = bundleEnvironmentOptions; } public BundleEnvironmentOptionsBuilder ForDebug(Action debugBuilder) { - if (debugBuilder == null) throw new ArgumentNullException(nameof(debugBuilder)); - _debugBuilder = debugBuilder; - return this; + return ForProfile(SmidgeOptionsProfile.Debug, debugBuilder); } public BundleEnvironmentOptionsBuilder ForProduction(Action productionBuilder) { - if (productionBuilder == null) throw new ArgumentNullException(nameof(productionBuilder)); - _productionBuilder = productionBuilder; - return this; + return ForProfile(SmidgeOptionsProfile.Production, productionBuilder); } + public BundleEnvironmentOptionsBuilder ForProfile(string profileName, Action profileOptionsBuilder) + { + if (string.IsNullOrEmpty(profileName)) + throw new ArgumentNullException(nameof(profileName)); + + if (profileOptionsBuilder == null) + throw new ArgumentNullException(nameof(profileOptionsBuilder)); + + _profileBuilders.Add(profileName, profileOptionsBuilder); + return this; + } + /// /// Builds the bundle environment options based on the callbacks specified /// @@ -39,16 +50,13 @@ public BundleEnvironmentOptions Build() { if (!_built) { - if (_debugBuilder != null) - { - _debugBuilder(new BundleOptionsBuilder(_bundleEnvironmentOptions.DebugOptions)); - } - if (_productionBuilder != null) + foreach (var (profileName, profileBuilder) in _profileBuilders) { - _productionBuilder(new BundleOptionsBuilder(_bundleEnvironmentOptions.ProductionOptions)); + BundleOptions options = _bundleEnvironmentOptions[profileName]; + profileBuilder.Invoke(new BundleOptionsBuilder(options)); } } return _bundleEnvironmentOptions; } } -} \ No newline at end of file +} From f4eba032c6887415d2c994756fd8edc600846542 Mon Sep 17 00:00:00 2001 From: Greg White Date: Sun, 21 Aug 2022 17:01:29 +1200 Subject: [PATCH 07/10] Allow fluent configuration of bundles to explicitly choose a configured profile to use rather than determining the profile at runtime. --- src/Smidge.Core/Models/Bundle.cs | 22 +++++++++++++++++++++- src/Smidge/SmidgeHelper.cs | 6 +++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Smidge.Core/Models/Bundle.cs b/src/Smidge.Core/Models/Bundle.cs index 61d670c..a8ac5fe 100644 --- a/src/Smidge.Core/Models/Bundle.cs +++ b/src/Smidge.Core/Models/Bundle.cs @@ -45,10 +45,30 @@ public Bundle(string name, List files, BundleEnvironmentOptions bundle /// public Func, IEnumerable> OrderingCallback { get; private set; } + + + /// + /// The name of the Profile used to configure the BundleOptions for this bundle. + /// If a BundleOptions has also been specified, the ProfileName will have no effect. + /// + public string ProfileName { get; set; } + + /// + /// Set the name of the Profile to use for this bundle. + /// + /// Name of the profile. + public Bundle UseProfile(string profileName) + { + ProfileName = profileName; + return this; + } + + + /// /// Defines the options for this bundle /// - public BundleEnvironmentOptions BundleOptions { get; private set; } + public BundleEnvironmentOptions BundleOptions { get; private set; } /// /// Sets the options for the bundle diff --git a/src/Smidge/SmidgeHelper.cs b/src/Smidge/SmidgeHelper.cs index d0a02c7..e18a977 100644 --- a/src/Smidge/SmidgeHelper.cs +++ b/src/Smidge/SmidgeHelper.cs @@ -197,8 +197,12 @@ private IEnumerable GenerateBundleUrlsAsync(string bundleName, string fi } else { - profileName = _profileStrategy.GetCurrentProfileName(); + // If the Bundle explicitly specifies a profile to use then use it otherwise use the current profile + profileName = !string.IsNullOrEmpty(bundle.ProfileName) + ? bundle.ProfileName + : _profileStrategy.GetCurrentProfileName(); } + //get the bundle options from the bundle if they have been set otherwise with the defaults var bundleOptions = bundle.GetBundleOptions(_bundleManager, profileName); From 1a725bd3f61413eaf505f1749cf8d37d612d205d Mon Sep 17 00:00:00 2001 From: Greg White Date: Sun, 21 Aug 2022 18:45:54 +1200 Subject: [PATCH 08/10] Updated SmidgeController and action filters to use configured bundle profiles. --- .../Options/BundleEnvironmentOptions.cs | 14 ++++++- .../AddCompressionHeaderAttribute.cs | 27 +++++++++++-- .../Controllers/AddExpiryHeadersAttribute.cs | 40 +++++++++++++++---- src/Smidge/Controllers/SmidgeController.cs | 25 +++++++++++- src/Smidge/SmidgeHelper.cs | 7 +++- test/Smidge.Tests/SmidgeHelperTests.cs | 14 ++++--- 6 files changed, 104 insertions(+), 23 deletions(-) diff --git a/src/Smidge.Core/Options/BundleEnvironmentOptions.cs b/src/Smidge.Core/Options/BundleEnvironmentOptions.cs index 40c360d..b7aae59 100644 --- a/src/Smidge.Core/Options/BundleEnvironmentOptions.cs +++ b/src/Smidge.Core/Options/BundleEnvironmentOptions.cs @@ -68,6 +68,7 @@ public BundleOptions ProductionOptions /// /// Gets or sets the for the specified profile. + /// If the profile has not been previously configured, returns a new instance. /// /// Name of the profile. /// @@ -75,7 +76,7 @@ public BundleOptions this[string profileName] { get { - if (!_profileOptions.TryGetValue(profileName, out BundleOptions options)) + if (!TryGetProfileOptions(profileName, out BundleOptions options)) { // Initialise a new BundleOptions for the requested profile options = new BundleOptions(); @@ -88,5 +89,16 @@ public BundleOptions this[string profileName] } + /// + /// Gets the for the specified profile, if the profile has previously been configured. + /// + /// Name of the profile. + /// The profile options. + /// + public bool TryGetProfileOptions(string profileName, out BundleOptions options) + { + return _profileOptions.TryGetValue(profileName, out options); + } + } } diff --git a/src/Smidge/Controllers/AddCompressionHeaderAttribute.cs b/src/Smidge/Controllers/AddCompressionHeaderAttribute.cs index f2ca3fb..0c472d8 100644 --- a/src/Smidge/Controllers/AddCompressionHeaderAttribute.cs +++ b/src/Smidge/Controllers/AddCompressionHeaderAttribute.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Linq; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; using Smidge.Models; +using Smidge.Options; namespace Smidge.Controllers { @@ -17,6 +18,7 @@ public sealed class AddCompressionHeaderAttribute : Attribute, IFilterFactory, I public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) { return new AddCompressionFilter( + serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()); } @@ -27,11 +29,13 @@ public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) private class AddCompressionFilter : IActionFilter { + private readonly ISmidgeProfileStrategy _profileStrategy; private readonly IRequestHelper _requestHelper; private readonly IBundleManager _bundleManager; - public AddCompressionFilter(IRequestHelper requestHelper, IBundleManager bundleManager) + public AddCompressionFilter(ISmidgeProfileStrategy profileStrategy, IRequestHelper requestHelper, IBundleManager bundleManager) { + _profileStrategy = profileStrategy ?? throw new ArgumentNullException(nameof(profileStrategy)); _requestHelper = requestHelper ?? throw new ArgumentNullException(nameof(requestHelper)); _bundleManager = bundleManager ?? throw new ArgumentNullException(nameof(bundleManager)); } @@ -62,7 +66,22 @@ public void OnActionExecuted(ActionExecutedContext context) //check if it's a bundle (not composite file) if (file is BundleRequestModel bundleRequest && _bundleManager.TryGetValue(bundleRequest.FileKey, out var bundle)) { - var bundleOptions = bundle.GetBundleOptions(_bundleManager, bundleRequest.Debug); + string profileName; + + // For backwards compatibility we'll use the Debug profile if it was explicitly requested in the request. + if (bundleRequest.Debug) + { + profileName = SmidgeOptionsProfile.Debug; + } + else + { + // If the Bundle explicitly specifies a profile to use then use it otherwise use the current profile + profileName = !string.IsNullOrEmpty(bundle.ProfileName) + ? bundle.ProfileName + : _profileStrategy.GetCurrentProfileName(); + } + + var bundleOptions = bundle.GetBundleOptions(_bundleManager, profileName); enableCompression = bundleOptions.CompressResult; } @@ -72,4 +91,4 @@ public void OnActionExecuted(ActionExecutedContext context) } } } -} \ No newline at end of file +} diff --git a/src/Smidge/Controllers/AddExpiryHeadersAttribute.cs b/src/Smidge/Controllers/AddExpiryHeadersAttribute.cs index 2929d58..b1db4c5 100644 --- a/src/Smidge/Controllers/AddExpiryHeadersAttribute.cs +++ b/src/Smidge/Controllers/AddExpiryHeadersAttribute.cs @@ -13,7 +13,10 @@ namespace Smidge.Controllers /// public sealed class AddExpiryHeadersAttribute : Attribute, IFilterFactory, IOrderedFilter { - public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) => new AddExpiryHeaderFilter(serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService()); + public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) => new AddExpiryHeaderFilter( + serviceProvider.GetRequiredService(), + serviceProvider.GetRequiredService(), + serviceProvider.GetRequiredService()); public bool IsReusable => true; @@ -21,13 +24,15 @@ public sealed class AddExpiryHeadersAttribute : Attribute, IFilterFactory, IOrde public sealed class AddExpiryHeaderFilter : IActionFilter { + private readonly ISmidgeProfileStrategy _profileStrategy; private readonly IHasher _hasher; private readonly IBundleManager _bundleManager; - public AddExpiryHeaderFilter(IHasher hasher, IBundleManager bundleManager) + public AddExpiryHeaderFilter(ISmidgeProfileStrategy profileStrategy, IHasher hasher, IBundleManager bundleManager) { - _hasher = hasher; - _bundleManager = bundleManager; + _profileStrategy = profileStrategy ?? throw new ArgumentNullException(nameof(profileStrategy)); + _hasher = hasher ?? throw new ArgumentNullException(nameof(hasher)); + _bundleManager = bundleManager ?? throw new ArgumentNullException(nameof(bundleManager)); } public void OnActionExecuting(ActionExecutingContext context) @@ -56,15 +61,34 @@ public void OnActionExecuted(ActionExecutedContext context) var enableETag = true; var cacheControlMaxAge = 10 * 24; //10 days + string profileName; BundleOptions bundleOptions; - - if (_bundleManager.TryGetValue(file.FileKey, out Bundle b)) + + if (_bundleManager.TryGetValue(file.FileKey, out Bundle bundle)) { - bundleOptions = b.GetBundleOptions(_bundleManager, file.Debug); + // For backwards compatibility we'll use the Debug profile if it was explicitly requested in the request. + if (file.Debug) + { + profileName = SmidgeOptionsProfile.Debug; + } + else + { + // If the Bundle explicitly specifies a profile to use then use it otherwise use the current profile + profileName = !string.IsNullOrEmpty(bundle.ProfileName) + ? bundle.ProfileName + : _profileStrategy.GetCurrentProfileName(); + } + + bundleOptions = bundle.GetBundleOptions(_bundleManager, profileName); } else { - bundleOptions = file.Debug ? _bundleManager.DefaultBundleOptions.DebugOptions : _bundleManager.DefaultBundleOptions.ProductionOptions; + // For backwards compatibility we'll use the Debug profile if it was explicitly requested in the request. + profileName = file.Debug + ? SmidgeOptionsProfile.Debug + : _profileStrategy.GetCurrentProfileName(); + + _bundleManager.DefaultBundleOptions.TryGetProfileOptions(profileName, out bundleOptions); } if (bundleOptions != null) diff --git a/src/Smidge/Controllers/SmidgeController.cs b/src/Smidge/Controllers/SmidgeController.cs index d053ad8..6d10a1e 100644 --- a/src/Smidge/Controllers/SmidgeController.cs +++ b/src/Smidge/Controllers/SmidgeController.cs @@ -12,6 +12,8 @@ using Smidge.FileProcessors; using Smidge.Cache; using Microsoft.AspNetCore.Authorization; +using System.Reflection.Metadata; +using Smidge.Options; namespace Smidge.Controllers { @@ -26,6 +28,7 @@ namespace Smidge.Controllers [AllowAnonymous] public class SmidgeController : Controller { + private readonly ISmidgeProfileStrategy _profileStrategy; private readonly ISmidgeFileSystem _fileSystem; private readonly IBundleManager _bundleManager; private readonly IBundleFileSetGenerator _fileSetGenerator; @@ -43,6 +46,7 @@ public class SmidgeController : Controller /// /// public SmidgeController( + ISmidgeProfileStrategy profileStrategy, ISmidgeFileSystem fileSystemHelper, IBundleManager bundleManager, IBundleFileSetGenerator fileSetGenerator, @@ -50,6 +54,7 @@ public SmidgeController( IPreProcessManager preProcessManager, ILogger logger) { + _profileStrategy = profileStrategy ?? throw new ArgumentNullException(nameof(profileStrategy)); _fileSystem = fileSystemHelper ?? throw new ArgumentNullException(nameof(fileSystemHelper)); _bundleManager = bundleManager ?? throw new ArgumentNullException(nameof(bundleManager)); _fileSetGenerator = fileSetGenerator ?? throw new ArgumentNullException(nameof(fileSetGenerator)); @@ -71,8 +76,24 @@ public async Task Bundle( return NotFound(); } - var bundleOptions = foundBundle.GetBundleOptions(_bundleManager, bundleModel.Debug); + string profileName; + // For backwards compatibility we'll use the Debug profile if it was explicitly requested in the request. + if (bundleModel.Debug) + { + profileName = SmidgeOptionsProfile.Debug; + } + else + { + // If the Bundle explicitly specifies a profile to use then use it otherwise use the current profile + profileName = !string.IsNullOrEmpty(foundBundle.ProfileName) + ? foundBundle.ProfileName + : _profileStrategy.GetCurrentProfileName(); + } + + //get the bundle options from the bundle if they have been set otherwise with the defaults + var bundleOptions = foundBundle.GetBundleOptions(_bundleManager, profileName); + var cacheBusterValue = bundleModel.ParsedPath.CacheBusterValue; //now we need to determine if this bundle has already been created @@ -164,7 +185,7 @@ public async Task Composite( return NotFound(); } - var defaultBundleOptions = _bundleManager.GetDefaultBundleOptions(false); + //var defaultBundleOptions = _bundleManager.GetDefaultBundleOptions(false); var cacheBusterValue = file.ParsedPath.CacheBusterValue; var cacheFile = _fileSystem.CacheFileSystem.GetCachedCompositeFile(cacheBusterValue, file.Compression, file.FileKey, out var cacheFilePath); diff --git a/src/Smidge/SmidgeHelper.cs b/src/Smidge/SmidgeHelper.cs index e18a977..73d818c 100644 --- a/src/Smidge/SmidgeHelper.cs +++ b/src/Smidge/SmidgeHelper.cs @@ -217,11 +217,14 @@ private IEnumerable GenerateBundleUrlsAsync(string bundleName, string fi _processorFactory.CreateDefault( //the file type in the bundle will always be the same bundle.Files[0].DependencyType)); - result.AddRange(files.Select(d => _urlManager.AppendCacheBuster(_requestHelper.Content(d), !bundleOptions.ProcessAsCompositeFile, cacheBusterValue))); + + // For backwards compatibility we'll only generate a debug token in the url if Debug was explicitly requested. + result.AddRange(files.Select(d => _urlManager.AppendCacheBuster(_requestHelper.Content(d), debug is true, cacheBusterValue))); return result; } - var url = _urlManager.GetUrl(bundleName, fileExt, !bundleOptions.ProcessAsCompositeFile, cacheBusterValue); + // For backwards compatibility we'll only generate a debug token in the url if Debug was explicitly requested. + var url = _urlManager.GetUrl(bundleName, fileExt, debug is true, cacheBusterValue); if (!string.IsNullOrWhiteSpace(url)) { result.Add(url); diff --git a/test/Smidge.Tests/SmidgeHelperTests.cs b/test/Smidge.Tests/SmidgeHelperTests.cs index 87a5d65..e16570a 100644 --- a/test/Smidge.Tests/SmidgeHelperTests.cs +++ b/test/Smidge.Tests/SmidgeHelperTests.cs @@ -170,12 +170,13 @@ public async Task Generate_Css_Urls_Returns_Multiple_Urls_When_Debug_Profile_Is_ var urls = await sut.GenerateJsUrlsAsync("test"); - Assert.Equal("/file1.css?d=00000", urls.ElementAtOrDefault(0)); - Assert.Equal("/file2.css?d=00000", urls.ElementAtOrDefault(1)); + Assert.Equal("/file1.css?v=00000", urls.ElementAtOrDefault(0)); + Assert.Equal("/file2.css?v=00000", urls.ElementAtOrDefault(1)); } + [Fact] - public async Task Generate_Css_Urls_Returns_Multiple_Urls_When_Debug_Parameter_Overrides_Profile() + public async Task Generate_Css_Urls_Returns_Urls_With_Debug_Token_When_Debug_Parameter_Overrides_Profile() { var sut = new SmidgeHelper( FakeProfileStrategy.DefaultProfileStrategy, @@ -284,12 +285,13 @@ public async Task Generate_Js_Urls_Returns_Multiple_Urls_When_Debug_Profile_Is_U var urls = await sut.GenerateJsUrlsAsync("test"); - Assert.Equal("/file1.js?d=00000", urls.ElementAtOrDefault(0)); - Assert.Equal("/file2.js?d=00000", urls.ElementAtOrDefault(1)); + Assert.Equal("/file1.js?v=00000", urls.ElementAtOrDefault(0)); + Assert.Equal("/file2.js?v=00000", urls.ElementAtOrDefault(1)); } + [Fact] - public async Task Generate_Js_Urls_Returns_Multiple_Urls_When_Debug_Parameter_Overrides_Profile() + public async Task Generate_Js_Urls_Returns_Urls_With_Debug_Token_When_Debug_Parameter_Overrides_Profile() { var sut = new SmidgeHelper( FakeProfileStrategy.DefaultProfileStrategy, From 5c9ca9a19e4817bee31ea94177ec89ce8354b278 Mon Sep 17 00:00:00 2001 From: Greg White Date: Tue, 13 Dec 2022 15:47:00 +1300 Subject: [PATCH 09/10] Tidy up mock objects. --- test/Smidge.Tests/SmidgeHelperTests.cs | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/test/Smidge.Tests/SmidgeHelperTests.cs b/test/Smidge.Tests/SmidgeHelperTests.cs index e16570a..0528bb8 100644 --- a/test/Smidge.Tests/SmidgeHelperTests.cs +++ b/test/Smidge.Tests/SmidgeHelperTests.cs @@ -22,7 +22,6 @@ namespace Smidge.Tests { public class SmidgeHelperTests { - private readonly IUrlManager _urlManager;// = Mock.Of(); private readonly IFileProvider _fileProvider = Mock.Of(); private readonly ICacheFileSystem _cacheProvider = Mock.Of(); private readonly IHasher _hasher = Mock.Of(); @@ -31,24 +30,25 @@ public class SmidgeHelperTests private readonly DynamicallyRegisteredWebFiles _dynamicallyRegisteredWebFiles; private readonly SmidgeFileSystem _fileSystemHelper; private readonly PreProcessManager _preProcessManager; - private Mock> _smidgeOptions; private readonly PreProcessPipelineFactory _processorFactory; private readonly IBundleManager _bundleManager; private readonly IRequestHelper _requestHelper; + private readonly IUrlManager _urlManager; private readonly CacheBusterResolver _cacheBusterResolver; - private readonly Mock _httpContextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; public SmidgeHelperTests() { - _httpContextAccessor = new Mock(); - _httpContextAccessor.Setup(x => x.HttpContext).Returns(Mock.Of); - + var httpContextAccessor = new Mock(); + httpContextAccessor.Setup(x => x.HttpContext).Returns(Mock.Of); + _httpContextAccessor = httpContextAccessor.Object; + _dynamicallyRegisteredWebFiles = new DynamicallyRegisteredWebFiles(); _fileSystemHelper = new SmidgeFileSystem(_fileProvider, new DefaultFileProviderFilter(), _cacheProvider, new FakeWebsiteInfo()); - _smidgeOptions = new Mock>(); - _smidgeOptions.Setup(opt => opt.Value).Returns(() => + var smidgeOptions = new Mock>(); + smidgeOptions.Setup(opt => opt.Value).Returns(() => { var options = new SmidgeOptions { @@ -61,19 +61,19 @@ public SmidgeHelperTests() }); _requestHelper = new RequestHelper(new FakeWebsiteInfo()); - _urlManager = new DefaultUrlManager(_smidgeOptions.Object, _hasher, _requestHelper); + _urlManager = new DefaultUrlManager(smidgeOptions.Object, _hasher, _requestHelper); _cacheBusterResolver = new CacheBusterResolver(FakeCacheBuster.Instances); _processorFactory = new PreProcessPipelineFactory(new Lazy>(() => _preProcessors)); - _bundleManager = new BundleManager(_smidgeOptions.Object, Mock.Of>()); + _bundleManager = new BundleManager(smidgeOptions.Object, Mock.Of>()); _preProcessManager = new PreProcessManager( _fileSystemHelper, _bundleManager, Mock.Of>()); _fileSetGenerator = new BundleFileSetGenerator( _fileSystemHelper, - new FileProcessingConventions(_smidgeOptions.Object, new List())); + new FileProcessingConventions(smidgeOptions.Object, new List())); } [Fact] @@ -84,7 +84,7 @@ public async Task JsHereAsync_Returns_Empty_String_Result_When_No_Files_Found() _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, _cacheBusterResolver); + _httpContextAccessor, _cacheBusterResolver); _bundleManager.CreateJs("empty", Array.Empty()); @@ -100,7 +100,7 @@ public async Task Generate_Css_Urls_For_Non_Existent_Bundle_Throws_Exception() _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, _cacheBusterResolver); + _httpContextAccessor, _cacheBusterResolver); var exception = await Assert.ThrowsAsync ( @@ -120,7 +120,7 @@ public async Task Generate_Css_Urls_Returns_SingleBundleUrl_When_Default_Profile _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, _cacheBusterResolver); + _httpContextAccessor, _cacheBusterResolver); _bundleManager.CreateCss("test", new[] { @@ -151,7 +151,7 @@ public async Task Generate_Css_Urls_Returns_Multiple_Urls_When_Debug_Profile_Is_ _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, _cacheBusterResolver); + _httpContextAccessor, _cacheBusterResolver); _bundleManager.CreateCss("test", new[] { @@ -183,7 +183,7 @@ public async Task Generate_Css_Urls_Returns_Urls_With_Debug_Token_When_Debug_Par _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, _cacheBusterResolver); + _httpContextAccessor, _cacheBusterResolver); _bundleManager.CreateCss("test", new[] { @@ -216,7 +216,7 @@ public async Task Generate_Js_Urls_For_Non_Existent_Bundle_Throws_Exception() _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, _cacheBusterResolver); + _httpContextAccessor, _cacheBusterResolver); var exception = await Assert.ThrowsAsync ( @@ -235,7 +235,7 @@ public async Task Generate_Js_Urls_Returns_SingleBundleUrl_When_Default_Profile_ _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, _cacheBusterResolver); + _httpContextAccessor, _cacheBusterResolver); _bundleManager.CreateJs("test", new[] { @@ -266,7 +266,7 @@ public async Task Generate_Js_Urls_Returns_Multiple_Urls_When_Debug_Profile_Is_U _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, _cacheBusterResolver); + _httpContextAccessor, _cacheBusterResolver); _bundleManager.CreateJs("test", new[] { @@ -298,7 +298,7 @@ public async Task Generate_Js_Urls_Returns_Urls_With_Debug_Token_When_Debug_Para _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, _cacheBusterResolver); + _httpContextAccessor, _cacheBusterResolver); _bundleManager.CreateJs("test", new[] { @@ -332,7 +332,7 @@ public async Task CssHere_HtmlString_For_Non_Existent_Css_Bundle_Throws_Exceptio _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, _cacheBusterResolver); + _httpContextAccessor, _cacheBusterResolver); var exception = await Assert.ThrowsAsync ( @@ -356,7 +356,7 @@ public async Task JsHere_HtmlString_For_Non_Existent_Css_Bundle_Throws_Exception _fileSetGenerator, _dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper, _hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper, - _httpContextAccessor.Object, _cacheBusterResolver); + _httpContextAccessor, _cacheBusterResolver); var exception = await Assert.ThrowsAsync ( From aa2770f1844371c5235ed626ed307368fecbac1d Mon Sep 17 00:00:00 2001 From: Greg White Date: Mon, 9 Oct 2023 17:31:54 +1300 Subject: [PATCH 10/10] Fixed compilation of unit tests --- test/Smidge.Tests/SmidgeHelperTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/Smidge.Tests/SmidgeHelperTests.cs b/test/Smidge.Tests/SmidgeHelperTests.cs index 0528bb8..a0d67bb 100644 --- a/test/Smidge.Tests/SmidgeHelperTests.cs +++ b/test/Smidge.Tests/SmidgeHelperTests.cs @@ -46,6 +46,9 @@ public SmidgeHelperTests() _dynamicallyRegisteredWebFiles = new DynamicallyRegisteredWebFiles(); _fileSystemHelper = new SmidgeFileSystem(_fileProvider, new DefaultFileProviderFilter(), _cacheProvider, new FakeWebsiteInfo()); + + var smidgeConfig = new Mock(); + smidgeConfig.Setup(c => c.KeepFileExtensions).Returns(false); var smidgeOptions = new Mock>(); smidgeOptions.Setup(opt => opt.Value).Returns(() => @@ -61,7 +64,7 @@ public SmidgeHelperTests() }); _requestHelper = new RequestHelper(new FakeWebsiteInfo()); - _urlManager = new DefaultUrlManager(smidgeOptions.Object, _hasher, _requestHelper); + _urlManager = new DefaultUrlManager(smidgeOptions.Object, _hasher, _requestHelper, smidgeConfig.Object); _cacheBusterResolver = new CacheBusterResolver(FakeCacheBuster.Instances);