From 63dcff2fc9a7e2ad85f3c84113a1f677ad35a339 Mon Sep 17 00:00:00 2001 From: LaazyJ Date: Sat, 25 Feb 2012 20:28:51 +0000 Subject: [PATCH 1/5] Support for configuring default minifiers for CSS and Javascript bundles. Use the fluent configuration syntax to configure default minifies when configuring bundles. For instance to use YUI as the default minifier for both CSS and JS: Bundle.ConfigureDefaults() .UseYuiForCssMinification() .UseYuiForJsMinification(); --- SquishIt.Framework/Bundle.cs | 5 + SquishIt.Framework/Configuration.cs | 97 + SquishIt.Framework/Css/CssBundle.cs | 2 +- .../JavaScript/JavaScriptBundle.cs | 2 +- SquishIt.Framework/SquishIt.Framework.csproj | 1 + .../Utilities/DebugStatusReader.cs | 2 +- SquishIt.Tests/CssBundleTests.cs | 2198 ++++++++--------- 7 files changed, 1205 insertions(+), 1102 deletions(-) create mode 100644 SquishIt.Framework/Configuration.cs diff --git a/SquishIt.Framework/Bundle.cs b/SquishIt.Framework/Bundle.cs index 012cc25..41d5d47 100644 --- a/SquishIt.Framework/Bundle.cs +++ b/SquishIt.Framework/Bundle.cs @@ -24,5 +24,10 @@ public static CSSBundle Css(Utilities.IDebugStatusReader debugStatusReader) { return new CSSBundle(debugStatusReader); } + + public static Configuration ConfigureDefaults() + { + return new Configuration(); + } } } \ No newline at end of file diff --git a/SquishIt.Framework/Configuration.cs b/SquishIt.Framework/Configuration.cs new file mode 100644 index 0000000..fb16f3c --- /dev/null +++ b/SquishIt.Framework/Configuration.cs @@ -0,0 +1,97 @@ +using System; +using SquishIt.Framework.Css; +using SquishIt.Framework.JavaScript; +using SquishIt.Framework.Minifiers; +using SquishIt.Framework.Minifiers.CSS; +using SquishIt.Framework.Minifiers.JavaScript; + +namespace SquishIt.Framework +{ + public class Configuration + { + /// + /// Use Yahoo YUI Compressor for CSS minification by default. + /// + public Configuration UseYuiForCssMinification() + { + _defaultCssMinifier = typeof (YuiCompressor); + return this; + } + + /// + /// Use Microsoft Ajax Minifier for CSS minification by default. + /// + public Configuration UseMsAjaxForCssMinification() + { + _defaultCssMinifier = typeof (MsCompressor); + return this; + } + + /// + /// By default, perform no minification of CSS. + /// + public Configuration UseNoCssMinification() + { + _defaultCssMinifier = typeof (NullCompressor); + return this; + } + + /// + /// Use Microsoft Ajax Minifier for Javascript minification by default. + /// + public Configuration UseMsAjaxForJsMinification() + { + _defaultJsMinifier = typeof (MsMinifier); + return this; + } + + /// + /// Use Yahoo YUI Compressor for Javascript minification by default. + /// + public Configuration UseYuiForJsMinification() + { + _defaultJsMinifier = typeof (YuiMinifier); + return this; + } + + /// + /// Use Google Closure for Javascript minification by default. + /// + public Configuration UseClosureForMinification() + { + _defaultJsMinifier = typeof (ClosureMinifier); + return this; + } + + /// + /// By default, perform no minification of Javascript. + /// + public Configuration UseNoJsMinification() + { + _defaultJsMinifier = typeof (NullMinifier); + return this; + } + + /// + /// Use Douglas Crockford's JsMin for Javascript minification by default. + /// + public Configuration UseJsMinForJsMinification() + { + _defaultJsMinifier = typeof (JsMinMinifier); + return this; + } + + static Type _defaultCssMinifier = typeof (MsCompressor); + static Type _defaultJsMinifier = typeof (MsMinifier); + + internal static IMinifier DefaultCssMinifier() + { + return (IMinifier)Activator.CreateInstance(_defaultCssMinifier); + } + + public static IMinifier DefaultJsMinifier() + { + return (IMinifier)Activator.CreateInstance(_defaultJsMinifier); + } + } +} diff --git a/SquishIt.Framework/Css/CssBundle.cs b/SquishIt.Framework/Css/CssBundle.cs index 2453cfd..b2007fa 100644 --- a/SquishIt.Framework/Css/CssBundle.cs +++ b/SquishIt.Framework/Css/CssBundle.cs @@ -37,7 +37,7 @@ protected override string CachePrefix protected override IMinifier DefaultMinifier { - get { return new MsCompressor(); } + get { return Configuration.DefaultCssMinifier(); } } private HashSet _allowedExtensions = new HashSet { ".CSS", ".LESS" }; diff --git a/SquishIt.Framework/JavaScript/JavaScriptBundle.cs b/SquishIt.Framework/JavaScript/JavaScriptBundle.cs index 82d0c2b..c63b6f7 100644 --- a/SquishIt.Framework/JavaScript/JavaScriptBundle.cs +++ b/SquishIt.Framework/JavaScript/JavaScriptBundle.cs @@ -20,7 +20,7 @@ public class JavaScriptBundle : BundleBase protected override IMinifier DefaultMinifier { - get { return new MsMinifier(); } + get { return Configuration.DefaultJsMinifier(); } } private HashSet _allowedExtensions = new HashSet { ".JS", ".COFFEE" }; diff --git a/SquishIt.Framework/SquishIt.Framework.csproj b/SquishIt.Framework/SquishIt.Framework.csproj index 3d39ebf..7a74f97 100644 --- a/SquishIt.Framework/SquishIt.Framework.csproj +++ b/SquishIt.Framework/SquishIt.Framework.csproj @@ -88,6 +88,7 @@ + diff --git a/SquishIt.Framework/Utilities/DebugStatusReader.cs b/SquishIt.Framework/Utilities/DebugStatusReader.cs index d54d7b1..1d2086f 100644 --- a/SquishIt.Framework/Utilities/DebugStatusReader.cs +++ b/SquishIt.Framework/Utilities/DebugStatusReader.cs @@ -26,7 +26,7 @@ public bool IsDebuggingEnabled() { //check retail setting in machine.config //Thanks Dave Ward! http://www.encosia.com - Configuration machineConfig = ConfigurationManager.OpenMachineConfiguration(); + System.Configuration.Configuration machineConfig = ConfigurationManager.OpenMachineConfiguration(); var group = machineConfig.GetSectionGroup("system.web"); if (group != null) { diff --git a/SquishIt.Tests/CssBundleTests.cs b/SquishIt.Tests/CssBundleTests.cs index 5b8ab7f..d2b8370 100644 --- a/SquishIt.Tests/CssBundleTests.cs +++ b/SquishIt.Tests/CssBundleTests.cs @@ -1,1100 +1,1100 @@ -using System; -using System.IO; -using NUnit.Framework; -using SquishIt.Framework.Css; -using SquishIt.Framework.Minifiers.CSS; -using SquishIt.Framework.Files; -using SquishIt.Framework.Utilities; -using SquishIt.Tests.Helpers; -using SquishIt.Tests.Stubs; -using SquishIt.Framework.Tests.Mocks; -using SquishIt.Framework; - -namespace SquishIt.Tests -{ - [TestFixture] - public class CssBundleTests - { - private string css = TestUtilities.NormalizeLineEndings(@" li { - margin-bottom:0.1em; - margin-left:0; - margin-top:0.1em; - } - - th { - font-weight:normal; - vertical-align:bottom; - } - - .FloatRight { - float:right; - } - - .FloatLeft { - float:left; - }"); - - private string css2 = TestUtilities.NormalizeLineEndings(@" li { - margin-bottom:0.1em; - margin-left:0; - margin-top:0.1em; - } - - th { - font-weight:normal; - vertical-align:bottom; - }"); - - - private string cssLess = TestUtilities.NormalizeLineEndings(@"@brand_color: #4D926F; - - #header { - color: @brand_color; - } - - h2 { - color: @brand_color; - }"); - - private CssBundleFactory cssBundleFactory; - private IHasher hasher; - - [SetUp] - public void Setup() - { - cssBundleFactory = new CssBundleFactory(); - var retryableFileOpener = new RetryableFileOpener(); - hasher = new Hasher(retryableFileOpener); - } - - [Test] - public void CanAddMultiplePathFiles() - { - var cssBundle1 = cssBundleFactory - .WithDebuggingEnabled(true) - .Create(); - - var cssBundle2 = cssBundleFactory - .WithDebuggingEnabled(true) - .Create(); - - cssBundle1.Add("/css/first.css", "/css/second.css"); - cssBundle2.Add("/css/first.css").Add("/css/second.css"); - - var cssBundle1Assets = cssBundle1.bundleState.Assets; - var cssBundle2Assets = cssBundle1.bundleState.Assets; - - Assert.AreEqual(cssBundle1Assets.Count, cssBundle2Assets.Count); - for (var i = 0; i < cssBundle1Assets.Count; i++) - { - var assetBundle1 = cssBundle1Assets[i]; - var assetBundle2 = cssBundle2Assets[i]; - Assert.AreEqual(assetBundle1.LocalPath, assetBundle2.LocalPath); - Assert.AreEqual(assetBundle1.Order, assetBundle2.Order); - } - } - - [Test] - public void CanBundleCss() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .Create(); - - cssBundleFactory.FileReaderFactory.SetContents(css); - - string tag = cssBundle - - .Add("/css/first.css") - .Add("/css/second.css") - .Render("/css/output.css"); - - Assert.AreEqual("", tag); - Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); - Assert.AreEqual ("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\output.css")]); - } - - [Test] - public void CanBundleCssSpecifyingOutputLinkPath() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .Create(); - - cssBundleFactory.FileReaderFactory.SetContents(css); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .WithOutputBaseHref("http//subdomain.domain.com") - .Render("/css/output.css"); - - Assert.AreEqual("", tag); - Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); - Assert.AreEqual ("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output.css")]); - } - - [Test] - public void CanBundleCssVaryingOutputBaseHrefRendersIndependantUrl() - { - //Verify that depending on basehref, we get independantly cached and returned URLs - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .Create(); - - cssBundleFactory.FileReaderFactory.SetContents(css); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .WithOutputBaseHref("http//subdomain.domain.com") - .Render("/css/output.css"); - - CSSBundle cssBundleNoBaseHref = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .Create(); - - cssBundleFactory.FileReaderFactory.SetContents(css); - - string tagNoBaseHref = cssBundleNoBaseHref - .Add("/css/first.css") - .Add("/css/second.css") - .Render("/css/output.css"); - - Assert.AreEqual("", tag); - Assert.AreEqual("", tagNoBaseHref); - Console.WriteLine("WithBaseHref:" + tag); - Console.WriteLine("NoBaseHref:" + tagNoBaseHref); - } - - - [Test] - public void CanBundleCssWithQueryStringParameter() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithContents(css) - .WithDebuggingEnabled(false) - .Create(); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .Render("/css/output_querystring.css?v=1"); - - Assert.AreEqual("", tag); - } - - [Test] - public void CanBundleCssWithMediaAttribute() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .WithAttribute("media", "screen") - .Render("/css/css_with_media_output.css"); - - Assert.AreEqual("", tag); - Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); - Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}" - , cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\css_with_media_output.css")]); - } - - [Test] - public void CanBundleCssWithRemote() - { - //this is rendering tag correctly but incorrectly(?) merging both files - using (new ResolverFactoryScope (typeof(Framework.Resolvers.HttpResolver).FullName, StubResolver.ForFile("http://www.someurl.com/css/first.css"))) - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - string tag = cssBundle - .AddRemote("/css/first.css", "http://www.someurl.com/css/first.css") - .Add("/css/second.css") - .Render("/css/output_remote.css"); - Assert.AreEqual ("", tag); - Assert.AreEqual (1, cssBundleFactory.FileWriterFactory.Files.Count); - Assert.AreEqual ("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output_remote.css")]); - } - } - - [Test] - public void CanBundleCssWithEmbedded() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - string tag = cssBundle - .AddEmbeddedResource("/css/first.css", "SquishIt.Tests://EmbeddedResource.Embedded.css") - .Render("/css/output_embedded.css"); - - Assert.AreEqual ("", tag); - Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); - Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}" - , cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output_embedded.css")]); - } - - [Test] - public void CanDebugBundleCssWithEmbedded() - { - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(true) - .WithContents(css) - .Create(); - - string tag = cssBundle - .AddEmbeddedResource("/css/first.css", "SquishIt.Tests://EmbeddedResource.Embedded.css") - .Render("/css/output_embedded.css"); - - Assert.AreEqual("\n", TestUtilities.NormalizeLineEndings(tag)); - Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); - } - - [Test] - public void CanBundleCssWithLess() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(cssLess) - .Create(); - - string tag = cssBundle - .Add("~/css/test.less") - .Render("~/css/output.css"); - - string contents = cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output.css")]; - - Assert.AreEqual("", tag); - Assert.AreEqual("#header{color:#4d926f}h2{color:#4d926f}", contents); - } - - [Test] - public void CanBundleCssWithLessAndPathRewrites() - { - string css = - @"@brand_color: #4D926F; - #header { - color: @brand_color; - background-image: url(../image/mygif.gif); - } - "; - - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("~/css/something/test.less") - .Render("~/css/output_less_with_rewrites.css"); - - string contents = cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output_less_with_rewrites.css")]; - - Assert.AreEqual("#header{color:#4d926f;background-image:url(image/mygif.gif)}", contents); - } - - [Test] - public void CanBundleCssWithNestedLess() - { - string importCss = - @" - @import 'other.less'; - #header { - color: #4D926F; - }"; - - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(importCss) - .Create(); - - TestUtilities.CreateFile("other.less", "#footer{color:#ffffff}"); - - cssBundle - .Add("~/css/test.less") - .Render("~/css/output_test.css"); - - TestUtilities.DeleteFile("other.less"); - - Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\output_test.css")]); - Assert.Contains(FileSystem.ResolveAppRelativePathToFileSystem("css/other.less"), cssBundle.DependentFiles); - } - - [Test] - public void CanBundleCssWithLessWithLessDotCssFileExtension() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(cssLess) - .Create(); - - string tag = cssBundle - .Add("~/css/test.less.css") - .Render("~/css/output_less_dot_css.css"); - - string contents = cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output_less_dot_css.css")]; - - Assert.AreEqual("", tag); - Assert.AreEqual("#header{color:#4d926f}h2{color:#4d926f}", contents); - } - - [Test] - public void CanCreateNamedBundle() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - cssBundle - .Add("~/css/temp.css") - .AsNamed("Test", "~/css/output.css"); - - string tag = cssBundle.RenderNamed("Test"); - - Assert.AreEqual ("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output.css")]); - Assert.AreEqual("", tag); - } - - [Test] - public void CanCreateNamedBundleWithDebug() - { - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(true) - .WithContents(css) - .Create(); - - cssBundle - .Add("~/css/temp1.css") - .Add("~/css/temp2.css") - .AsNamed("TestWithDebug", "~/css/output.css"); - - string tag = cssBundle.RenderNamed("TestWithDebug"); - - Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag)); - } - - [Test] - public void CanCreateNamedBundleWithMediaAttribute() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - cssBundle - .Add("~/css/temp.css") - .WithAttribute("media", "screen") - .AsNamed("TestWithMedia", "~/css/output.css"); - - string tag = cssBundle.RenderNamed("TestWithMedia"); - - Assert.AreEqual ("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output.css")]); - Assert.AreEqual("", tag); - } - - [Test] - public void CanRenderDebugTags() - { - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(true) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .Render("/css/output.css"); - - Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag)); - } - - [Test] - public void CanRenderDebugTagsTwice() - { - CSSBundle cssBundle1 = cssBundleFactory - .WithDebuggingEnabled(true) - .WithContents(css) - .Create(); - - CSSBundle cssBundle2 = cssBundleFactory - .WithDebuggingEnabled(true) - .WithContents(css) - .Create(); - - string tag1 = cssBundle1 - .Add("/css/first.css") - .Add("/css/second.css") - .Render("/css/output.css"); - - string tag2 = cssBundle2 - .Add("/css/first.css") - .Add("/css/second.css") - .Render("/css/output.css"); - - Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag1)); - Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag2)); - } - - [Test] - public void CanRenderDebugTagsWithMediaAttribute() - { - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(true) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .WithAttribute("media", "screen") - .Render("/css/output.css"); - - Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag)); - } - - [Test] - public void CanBundleCssWithCompressorAttribute() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .WithMinifier() - .Render("/css/css_with_compressor_output.css"); - - Assert.AreEqual("", tag); - Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); - Assert.AreEqual(" li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:400;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:400;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}" - , cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\css_with_compressor_output.css")]); - } - - [Test] - public void CanBundleCssWithNullCompressorAttribute() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .WithMinifier() - .Render("/css/css_with_null_compressor_output.css"); - - Assert.AreEqual ("", tag); - Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); - Assert.AreEqual (css + "\n" + css + "\n", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\css_with_null_compressor_output.css")]); - } - - [Test] - public void CanBundleCssWithCompressorInstance() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .Create(); - - cssBundleFactory.FileReaderFactory.SetContents(css); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .WithMinifier() - .Render("/css/compressor_instance.css"); - - Assert.AreEqual("", tag); - Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); - Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}" - , cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\compressor_instance.css")]); - } - - [Test] - public void CanRenderOnlyIfFileMissing() - { - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - cssBundleFactory.FileReaderFactory.SetFileExists(false); - - cssBundle - .Add("/css/first.css") - .Render("~/css/can_render_only_if_file_missing.css"); - - Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\can_render_only_if_file_missing.css")]); - - cssBundleFactory.FileReaderFactory.SetContents(css2); - cssBundleFactory.FileReaderFactory.SetFileExists(true); - cssBundle.ClearCache(); - - cssBundle - .Add("/css/first.css") - .RenderOnlyIfOutputFileMissing() - .Render("~/css/can_render_only_if_file_missing.css"); - - Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\can_render_only_if_file_missing.css")]); - } - - [Test] - public void CanRerenderFiles() - { - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - cssBundleFactory.FileReaderFactory.SetFileExists(false); - - cssBundle.ClearCache(); - cssBundle - .Add("/css/first.css") - .Render("~/css/can_rerender_files.css"); - - Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\can_rerender_files.css")]); - - CSSBundle cssBundle2 = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(css2) - .Create(); - - cssBundleFactory.FileReaderFactory.SetFileExists(true); - cssBundleFactory.FileWriterFactory.Files.Clear(); - cssBundle.ClearCache(); - - cssBundle2 - .Add("/css/first.css") - .Render("~/css/can_rerender_files.css"); - - Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\can_rerender_files.css")]); - } - - [Test] - public void CanRenderCssFileWithHashInFileName() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .Render("/css/output_#.css"); - - Assert.AreEqual("", tag); - Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); - Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\output_C33D1225DED9D889876CEE87754EE305.css")]); - } - - [Test] - public void CanRenderCssFileWithUnprocessedImportStatement() - { - string importCss = - @" - @import url(""/css/other.css""); - #header { - color: #4D926F; - }"; - - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(importCss) - .Create(); - - cssBundleFactory.FileReaderFactory.SetContents(importCss); - cssBundleFactory.FileReaderFactory.SetContentsForFile(@"C:\css\other.css", "#footer{color:#ffffff}"); - - cssBundle - .Add("/css/first.css") - .Render("/css/unprocessed_import.css"); - - Assert.AreEqual(@"@import url(""/css/other.css"");#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\unprocessed_import.css")]); - } - - [Test] - public void CanRenderCssFileWithImportStatement() - { - string importCss = - @" - @import url(""/css/other.css""); - #header { - color: #4D926F; - }"; - - - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(importCss) - .Create(); - - cssBundleFactory.FileReaderFactory.SetContents(importCss); - cssBundleFactory.FileReaderFactory.SetContentsForFile(TestUtilities.PrepareRelativePath(@"css\other.css"), "#footer{color:#ffffff}"); - - string tag = cssBundle - .Add("/css/first.css") - .ProcessImports() - .Render("/css/processed_import.css"); - - Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\processed_import.css")]); - } - - [Test] - public void CanRenderCssFileWithRelativeImportStatement() - { - string importCss = - @" - @import url(""other.css""); - #header { - color: #4D926F; - }"; - - - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(importCss) - .Create(); - - cssBundleFactory.FileReaderFactory.SetContents(importCss); - cssBundleFactory.FileReaderFactory.SetContentsForFile(TestUtilities.PrepareRelativePath(@"css\other.css"), "#footer{color:#ffffff}"); - - string tag = cssBundle - .Add("/css/first.css") - .ProcessImports() - .Render("/css/processed_import.css"); - - Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\processed_import.css")]); - } - - [Test] - public void CanRenderCssFileWithImportStatementNoQuotes() - { - string importCss = - @" - @import url(/css/other.css); - #header { - color: #4D926F; - }"; - - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(importCss) - .Create(); - - cssBundleFactory.FileReaderFactory.SetContentsForFile(TestUtilities.PrepareRelativePath(@"css\other.css"), "#footer{color:#ffffff}"); - - string tag = cssBundle - .Add("/css/first.css") - .ProcessImports() - .Render("/css/processed_import_noquotes.css"); - - Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\processed_import_noquotes.css")]); - } - - [Test] - public void CanRenderCssFileWithImportStatementSingleQuotes() - { - string importCss = - @" - @import url('/css/other.css'); - #header { - color: #4D926F; - }"; - - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(importCss) - .Create(); - - cssBundleFactory.FileReaderFactory.SetContentsForFile(TestUtilities.PrepareRelativePath(@"css\other.css"), "#footer{color:#ffffff}"); - - cssBundle - .Add("/css/first.css") - .ProcessImports() - .Render("/css/processed_import_singlequotes.css"); - - Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\processed_import_singlequotes.css")]); - } - - [Test] - public void CanRenderCssFileWithImportStatementUppercase() - { - string importCss = - @" - @IMPORT URL(/css/other.css); - #header { - color: #4D926F; - }"; - - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(importCss) - .Create(); - - cssBundleFactory.FileReaderFactory.SetContentsForFile(TestUtilities.PrepareRelativePath(@"css\other.css"), "#footer{color:#ffffff}"); - - string tag = cssBundle - .Add("/css/first.css") - .ProcessImports() - .Render("/css/processed_import_uppercase.css"); - - Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\processed_import_uppercase.css")]); - } - - [Test] - public void CanCreateNamedBundleWithForceRelease() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(true) - .WithContents(css) - .Create(); - - cssBundle - .Add("~/css/temp.css") - .ForceRelease() - .AsNamed("TestForce", "~/css/named_withforce.css"); - - string tag = cssBundle.RenderNamed("TestForce"); - - Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\named_withforce.css")]); - Assert.AreEqual("", tag); - } - - [Test] - public void CanBundleCssWithArbitraryAttributes() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .WithAttribute("media", "screen") - .WithAttribute("test", "other") - .Render("/css/css_with_attribute_output.css"); - - Assert.AreEqual("", tag); - } - - [Test] - public void CanBundleDebugCssWithArbitraryAttributes() - { - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(true) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("/css/first.css") - .Add("/css/second.css") - .WithAttribute("media", "screen") - .WithAttribute("test", "other") - .Render("/css/css_with_debugattribute_output.css"); - - Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag)); - } - - [Test] - public void CanCreateCachedBundle() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("~/css/temp.css") - .AsCached("TestCached", "~/static/css/TestCached.css"); - - string contents = cssBundle.RenderCached("TestCached"); - - Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", contents); - Assert.AreEqual("", tag); - } - - [Test] - public void CanCreateCachedBundleAssetTag() - { - CSSBundle cssBundle = cssBundleFactory - .WithHasher(hasher) - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - cssBundle - .Add("~/css/temp.css") - .AsCached("TestCached", "~/static/css/TestCached.css"); - - string contents = cssBundle.RenderCached("TestCached"); - cssBundle.ClearCache(); - string tag = cssBundle.RenderCachedAssetTag("TestCached"); - - Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", contents); - Assert.AreEqual("", tag); - } - - [Test] - public void CanCreateCachedBundleInDebugMode() - { - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(true) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("~/css/temp.css") - .AsCached("TestCached", "~/static/css/TestCached.css"); - - Assert.AreEqual("\n", TestUtilities.NormalizeLineEndings(tag)); - } - - [Test] - public void CanBundleDirectoryContentsInDebug() { - var path = Guid.NewGuid().ToString(); - var file1 = TestUtilities.PrepareRelativePath(path + "\\file1.css"); - var file2 = TestUtilities.PrepareRelativePath(path + "\\file2.css"); - - using (new ResolverFactoryScope(typeof(SquishIt.Framework.Resolvers.FileSystemResolver).FullName, StubResolver.ForDirectory(new[] { file1, file2 }))) { - var frf = new StubFileReaderFactory(); - frf.SetContentsForFile(file1, css2); - frf.SetContentsForFile(file2, css); - - var writerFactory = new StubFileWriterFactory(); - - var tag = cssBundleFactory.WithDebuggingEnabled(true) - .WithFileReaderFactory(frf) - .WithFileWriterFactory(writerFactory) - .WithHasher(new StubHasher("hashy")) - .Create() - .Add(path) - .Render("~/output.css"); - - var expectedTag = string.Format("\n\n", path); - Assert.AreEqual(expectedTag, TestUtilities.NormalizeLineEndings(tag)); - } - } - - [Test] - public void CanBundleDirectoryContentsInDebug_Ignores_Duplicates() { - var path = Guid.NewGuid().ToString(); - var file1 = TestUtilities.PrepareRelativePath(path + "\\file1.css"); - var file2 = TestUtilities.PrepareRelativePath(path + "\\file2.css"); - - using (new ResolverFactoryScope(typeof(SquishIt.Framework.Resolvers.FileSystemResolver).FullName, StubResolver.ForDirectory(new[] { file1, file2 }))) { - var frf = new StubFileReaderFactory(); - frf.SetContentsForFile(file1, css2); - frf.SetContentsForFile(file2, css); - - var writerFactory = new StubFileWriterFactory(); - - var tag = cssBundleFactory.WithDebuggingEnabled(true) - .WithFileReaderFactory(frf) - .WithFileWriterFactory(writerFactory) - .WithHasher(new StubHasher("hashy")) - .Create() - .Add(path) - .Render("~/output.css"); - - var expectedTag = string.Format("\n\n", path); - Assert.AreEqual(expectedTag, TestUtilities.NormalizeLineEndings(tag)); - } - } - - [Test] - public void CanBundleDirectoryContentsInRelease() { - var path = Guid.NewGuid().ToString(); - var file1 = TestUtilities.PrepareRelativePath(path + "\\file1.css"); - var file2 = TestUtilities.PrepareRelativePath(path + "\\file2.css"); - - using (new ResolverFactoryScope(typeof(SquishIt.Framework.Resolvers.FileSystemResolver).FullName, StubResolver.ForDirectory(new[] { file1, file2 }))) { - var frf = new StubFileReaderFactory(); - frf.SetContentsForFile(file1, css2); - frf.SetContentsForFile(file2, css); - - var writerFactory = new StubFileWriterFactory(); - - var tag = cssBundleFactory.WithDebuggingEnabled(false) - .WithFileReaderFactory(frf) - .WithFileWriterFactory(writerFactory) - .WithHasher(new StubHasher("hashy")) - .Create() - .Add(path) - .Render("~/output.css"); - - var expectedTag = ""; - Assert.AreEqual(expectedTag, tag); - - var combined = "li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}"; - Assert.AreEqual(combined, writerFactory.Files[TestUtilities.PrepareRelativePath(@"output.css")]); - } - } - - [Test] - public void CanBundleDirectoryContentsInRelease_Ignores_Duplicates() { - var path = Guid.NewGuid().ToString(); - var file1 = TestUtilities.PrepareRelativePath(path + "\\file1.css"); - var file2 = TestUtilities.PrepareRelativePath(path + "\\file2.css"); - - using (new ResolverFactoryScope(typeof(SquishIt.Framework.Resolvers.FileSystemResolver).FullName, StubResolver.ForDirectory(new[] { file1, file2 }))) { - var frf = new StubFileReaderFactory(); - frf.SetContentsForFile(file1, css2); - frf.SetContentsForFile(file2, css); - - var writerFactory = new StubFileWriterFactory(); - - var tag = cssBundleFactory.WithDebuggingEnabled(false) - .WithFileReaderFactory(frf) - .WithFileWriterFactory(writerFactory) - .WithHasher(new StubHasher("hashy")) - .Create() - .Add(path) - .Add(file1) - .Render("~/output.css"); - - var expectedTag = ""; - Assert.AreEqual(expectedTag, tag); - - var combined = "li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}"; - Assert.AreEqual(combined, writerFactory.Files[TestUtilities.PrepareRelativePath(@"output.css")]); - } - } - - [Test] - public void CanRenderArbitraryStringsInDebug () - { - var css2Format = "{0}{1}"; - - var hrColor = "hr {color:sienna;}"; - var p = "p {margin-left:20px;}"; - - var tag = new CssBundleFactory () - .WithDebuggingEnabled (true) - .Create () - .AddString (css) - .AddString (css2Format, hrColor, p) - .Render ("doesn't matter where..."); - - var expectedTag = string.Format ("\n\n", css, string.Format (css2Format, hrColor, p)); - Assert.AreEqual (expectedTag, TestUtilities.NormalizeLineEndings (tag)); - } - - [Test] - public void CanRenderArbitraryStringsInDebugWithoutType () - { - var css2Format = "{0}{1}"; - - var hrColor = "hr {color:sienna;}"; - var p = "p {margin-left:20px;}"; - - var tag = new CssBundleFactory () - .WithDebuggingEnabled (true) - .Create () - .AddString (css) - .AddString (css2Format, hrColor, p) - .WithoutTypeAttribute () - .Render ("doesn't matter where..."); - - var expectedTag = string.Format ("\n\n", css, string.Format (css2Format, hrColor, p)); - Assert.AreEqual (expectedTag, TestUtilities.NormalizeLineEndings (tag)); - } - - [Test] - public void DoesNotRenderDuplicateArbitraryStringsInDebug () - { - var tag = new CssBundleFactory () - .WithDebuggingEnabled (true) - .Create () - .AddString (css) - .AddString (css) - .Render ("doesn't matter where..."); - - var expectedTag = string.Format ("\n", css); - Assert.AreEqual (expectedTag, TestUtilities.NormalizeLineEndings (tag)); - } - - [Test] - public void CanBundleArbitraryContentsInRelease () - { - var css2Format = "{0}{1}"; - - var hrColor = "hr {color:sienna;}"; - var p = "p {margin-left:20px;}"; - - var writerFactory = new StubFileWriterFactory (); - - var tag = new CssBundleFactory () - .WithDebuggingEnabled (false) - .WithFileWriterFactory (writerFactory) - .WithHasher (new StubHasher ("hashy")) - .Create () - .AddString (css) - .AddString (css2Format, hrColor, p) - .Render ("~/output.css"); - - var expectedTag = ""; - Assert.AreEqual (expectedTag, TestUtilities.NormalizeLineEndings (tag)); - - var minifiedScript = "li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}hr{color:#a0522d}p{margin-left:20px}"; - Assert.AreEqual (minifiedScript, writerFactory.Files[TestUtilities.PrepareRelativePath("output.css")]); - } - - [Test] - public void PathRewritingDoesNotAffectClassesNamedUrl() - { - string css = - @" - a.url { - color: #4D926F; - } - "; - - CSSBundle cssBundle = cssBundleFactory - .WithDebuggingEnabled(false) - .WithContents(css) - .Create(); - - string tag = cssBundle - .Add("~/css/something/test.css") - .Render("~/css/output_rewriting_url.css"); - - string contents = cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\output_rewriting_url.css")]; - - Assert.AreEqual("a.url{color:#4d926f}", contents); - } - } +using System; +using System.IO; +using NUnit.Framework; +using SquishIt.Framework.Css; +using SquishIt.Framework.Minifiers.CSS; +using SquishIt.Framework.Files; +using SquishIt.Framework.Utilities; +using SquishIt.Tests.Helpers; +using SquishIt.Tests.Stubs; +using SquishIt.Framework.Tests.Mocks; +using SquishIt.Framework; + +namespace SquishIt.Tests +{ + [TestFixture] + public class CssBundleTests + { + private string css = TestUtilities.NormalizeLineEndings(@" li { + margin-bottom:0.1em; + margin-left:0; + margin-top:0.1em; + } + + th { + font-weight:normal; + vertical-align:bottom; + } + + .FloatRight { + float:right; + } + + .FloatLeft { + float:left; + }"); + + private string css2 = TestUtilities.NormalizeLineEndings(@" li { + margin-bottom:0.1em; + margin-left:0; + margin-top:0.1em; + } + + th { + font-weight:normal; + vertical-align:bottom; + }"); + + + private string cssLess = TestUtilities.NormalizeLineEndings(@"@brand_color: #4D926F; + + #header { + color: @brand_color; + } + + h2 { + color: @brand_color; + }"); + + private CssBundleFactory cssBundleFactory; + private IHasher hasher; + + [SetUp] + public void Setup() + { + cssBundleFactory = new CssBundleFactory(); + var retryableFileOpener = new RetryableFileOpener(); + hasher = new Hasher(retryableFileOpener); + } + + [Test] + public void CanAddMultiplePathFiles() + { + var cssBundle1 = cssBundleFactory + .WithDebuggingEnabled(true) + .Create(); + + var cssBundle2 = cssBundleFactory + .WithDebuggingEnabled(true) + .Create(); + + cssBundle1.Add("/css/first.css", "/css/second.css"); + cssBundle2.Add("/css/first.css").Add("/css/second.css"); + + var cssBundle1Assets = cssBundle1.bundleState.Assets; + var cssBundle2Assets = cssBundle1.bundleState.Assets; + + Assert.AreEqual(cssBundle1Assets.Count, cssBundle2Assets.Count); + for (var i = 0; i < cssBundle1Assets.Count; i++) + { + var assetBundle1 = cssBundle1Assets[i]; + var assetBundle2 = cssBundle2Assets[i]; + Assert.AreEqual(assetBundle1.LocalPath, assetBundle2.LocalPath); + Assert.AreEqual(assetBundle1.Order, assetBundle2.Order); + } + } + + [Test] + public void CanBundleCss() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .Create(); + + cssBundleFactory.FileReaderFactory.SetContents(css); + + string tag = cssBundle + + .Add("/css/first.css") + .Add("/css/second.css") + .Render("/css/output.css"); + + Assert.AreEqual("", tag); + Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); + Assert.AreEqual ("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\output.css")]); + } + + [Test] + public void CanBundleCssSpecifyingOutputLinkPath() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .Create(); + + cssBundleFactory.FileReaderFactory.SetContents(css); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .WithOutputBaseHref("http//subdomain.domain.com") + .Render("/css/output.css"); + + Assert.AreEqual("", tag); + Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); + Assert.AreEqual ("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output.css")]); + } + + [Test] + public void CanBundleCssVaryingOutputBaseHrefRendersIndependantUrl() + { + //Verify that depending on basehref, we get independantly cached and returned URLs + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .Create(); + + cssBundleFactory.FileReaderFactory.SetContents(css); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .WithOutputBaseHref("http//subdomain.domain.com") + .Render("/css/output.css"); + + CSSBundle cssBundleNoBaseHref = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .Create(); + + cssBundleFactory.FileReaderFactory.SetContents(css); + + string tagNoBaseHref = cssBundleNoBaseHref + .Add("/css/first.css") + .Add("/css/second.css") + .Render("/css/output.css"); + + Assert.AreEqual("", tag); + Assert.AreEqual("", tagNoBaseHref); + Console.WriteLine("WithBaseHref:" + tag); + Console.WriteLine("NoBaseHref:" + tagNoBaseHref); + } + + + [Test] + public void CanBundleCssWithQueryStringParameter() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithContents(css) + .WithDebuggingEnabled(false) + .Create(); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .Render("/css/output_querystring.css?v=1"); + + Assert.AreEqual("", tag); + } + + [Test] + public void CanBundleCssWithMediaAttribute() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .WithAttribute("media", "screen") + .Render("/css/css_with_media_output.css"); + + Assert.AreEqual("", tag); + Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); + Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}" + , cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\css_with_media_output.css")]); + } + + [Test] + public void CanBundleCssWithRemote() + { + //this is rendering tag correctly but incorrectly(?) merging both files + using (new ResolverFactoryScope (typeof(Framework.Resolvers.HttpResolver).FullName, StubResolver.ForFile("http://www.someurl.com/css/first.css"))) + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + string tag = cssBundle + .AddRemote("/css/first.css", "http://www.someurl.com/css/first.css") + .Add("/css/second.css") + .Render("/css/output_remote.css"); + Assert.AreEqual ("", tag); + Assert.AreEqual (1, cssBundleFactory.FileWriterFactory.Files.Count); + Assert.AreEqual ("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output_remote.css")]); + } + } + + [Test] + public void CanBundleCssWithEmbedded() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + string tag = cssBundle + .AddEmbeddedResource("/css/first.css", "SquishIt.Tests://EmbeddedResource.Embedded.css") + .Render("/css/output_embedded.css"); + + Assert.AreEqual ("", tag); + Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); + Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}" + , cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output_embedded.css")]); + } + + [Test] + public void CanDebugBundleCssWithEmbedded() + { + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(true) + .WithContents(css) + .Create(); + + string tag = cssBundle + .AddEmbeddedResource("/css/first.css", "SquishIt.Tests://EmbeddedResource.Embedded.css") + .Render("/css/output_embedded.css"); + + Assert.AreEqual("\n", TestUtilities.NormalizeLineEndings(tag)); + Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); + } + + [Test] + public void CanBundleCssWithLess() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(cssLess) + .Create(); + + string tag = cssBundle + .Add("~/css/test.less") + .Render("~/css/output.css"); + + string contents = cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output.css")]; + + Assert.AreEqual("", tag); + Assert.AreEqual("#header{color:#4d926f}h2{color:#4d926f}", contents); + } + + [Test] + public void CanBundleCssWithLessAndPathRewrites() + { + string css = + @"@brand_color: #4D926F; + #header { + color: @brand_color; + background-image: url(../image/mygif.gif); + } + "; + + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("~/css/something/test.less") + .Render("~/css/output_less_with_rewrites.css"); + + string contents = cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output_less_with_rewrites.css")]; + + Assert.AreEqual("#header{color:#4d926f;background-image:url(image/mygif.gif)}", contents); + } + + [Test] + public void CanBundleCssWithNestedLess() + { + string importCss = + @" + @import 'other.less'; + #header { + color: #4D926F; + }"; + + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(importCss) + .Create(); + + TestUtilities.CreateFile("other.less", "#footer{color:#ffffff}"); + + cssBundle + .Add("~/css/test.less") + .Render("~/css/output_test.css"); + + TestUtilities.DeleteFile("other.less"); + + Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\output_test.css")]); + Assert.Contains(FileSystem.ResolveAppRelativePathToFileSystem("css/other.less"), cssBundle.DependentFiles); + } + + [Test] + public void CanBundleCssWithLessWithLessDotCssFileExtension() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(cssLess) + .Create(); + + string tag = cssBundle + .Add("~/css/test.less.css") + .Render("~/css/output_less_dot_css.css"); + + string contents = cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output_less_dot_css.css")]; + + Assert.AreEqual("", tag); + Assert.AreEqual("#header{color:#4d926f}h2{color:#4d926f}", contents); + } + + [Test] + public void CanCreateNamedBundle() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + cssBundle + .Add("~/css/temp.css") + .AsNamed("Test", "~/css/output.css"); + + string tag = cssBundle.RenderNamed("Test"); + + Assert.AreEqual ("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output.css")]); + Assert.AreEqual("", tag); + } + + [Test] + public void CanCreateNamedBundleWithDebug() + { + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(true) + .WithContents(css) + .Create(); + + cssBundle + .Add("~/css/temp1.css") + .Add("~/css/temp2.css") + .AsNamed("TestWithDebug", "~/css/output.css"); + + string tag = cssBundle.RenderNamed("TestWithDebug"); + + Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag)); + } + + [Test] + public void CanCreateNamedBundleWithMediaAttribute() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + cssBundle + .Add("~/css/temp.css") + .WithAttribute("media", "screen") + .AsNamed("TestWithMedia", "~/css/output.css"); + + string tag = cssBundle.RenderNamed("TestWithMedia"); + + Assert.AreEqual ("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\output.css")]); + Assert.AreEqual("", tag); + } + + [Test] + public void CanRenderDebugTags() + { + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(true) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .Render("/css/output.css"); + + Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag)); + } + + [Test] + public void CanRenderDebugTagsTwice() + { + CSSBundle cssBundle1 = cssBundleFactory + .WithDebuggingEnabled(true) + .WithContents(css) + .Create(); + + CSSBundle cssBundle2 = cssBundleFactory + .WithDebuggingEnabled(true) + .WithContents(css) + .Create(); + + string tag1 = cssBundle1 + .Add("/css/first.css") + .Add("/css/second.css") + .Render("/css/output.css"); + + string tag2 = cssBundle2 + .Add("/css/first.css") + .Add("/css/second.css") + .Render("/css/output.css"); + + Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag1)); + Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag2)); + } + + [Test] + public void CanRenderDebugTagsWithMediaAttribute() + { + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(true) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .WithAttribute("media", "screen") + .Render("/css/output.css"); + + Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag)); + } + + [Test] + public void CanBundleCssWithCompressorAttribute() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .WithMinifier() + .Render("/css/css_with_compressor_output.css"); + + Assert.AreEqual("", tag); + Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); + Assert.AreEqual(" li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:400;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:400;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}" + , cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\css_with_compressor_output.css")]); + } + + [Test] + public void CanBundleCssWithNullCompressorAttribute() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .WithMinifier() + .Render("/css/css_with_null_compressor_output.css"); + + Assert.AreEqual ("", tag); + Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); + Assert.AreEqual (css + "\n" + css + "\n", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath (@"css\css_with_null_compressor_output.css")]); + } + + [Test] + public void CanBundleCssWithCompressorInstance() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .Create(); + + cssBundleFactory.FileReaderFactory.SetContents(css); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .WithMinifier() + .Render("/css/compressor_instance.css"); + + Assert.AreEqual("", tag); + Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); + Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}" + , cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\compressor_instance.css")]); + } + + [Test] + public void CanRenderOnlyIfFileMissing() + { + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + cssBundleFactory.FileReaderFactory.SetFileExists(false); + + cssBundle + .Add("/css/first.css") + .Render("~/css/can_render_only_if_file_missing.css"); + + Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\can_render_only_if_file_missing.css")]); + + cssBundleFactory.FileReaderFactory.SetContents(css2); + cssBundleFactory.FileReaderFactory.SetFileExists(true); + cssBundle.ClearCache(); + + cssBundle + .Add("/css/first.css") + .RenderOnlyIfOutputFileMissing() + .Render("~/css/can_render_only_if_file_missing.css"); + + Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\can_render_only_if_file_missing.css")]); + } + + [Test] + public void CanRerenderFiles() + { + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + cssBundleFactory.FileReaderFactory.SetFileExists(false); + + cssBundle.ClearCache(); + cssBundle + .Add("/css/first.css") + .Render("~/css/can_rerender_files.css"); + + Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\can_rerender_files.css")]); + + CSSBundle cssBundle2 = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(css2) + .Create(); + + cssBundleFactory.FileReaderFactory.SetFileExists(true); + cssBundleFactory.FileWriterFactory.Files.Clear(); + cssBundle.ClearCache(); + + cssBundle2 + .Add("/css/first.css") + .Render("~/css/can_rerender_files.css"); + + Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\can_rerender_files.css")]); + } + + [Test] + public void CanRenderCssFileWithHashInFileName() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .Render("/css/output_#.css"); + + Assert.AreEqual("", tag); + Assert.AreEqual(1, cssBundleFactory.FileWriterFactory.Files.Count); + Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\output_C33D1225DED9D889876CEE87754EE305.css")]); + } + + [Test] + public void CanRenderCssFileWithUnprocessedImportStatement() + { + string importCss = + @" + @import url(""/css/other.css""); + #header { + color: #4D926F; + }"; + + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(importCss) + .Create(); + + cssBundleFactory.FileReaderFactory.SetContents(importCss); + cssBundleFactory.FileReaderFactory.SetContentsForFile(@"C:\css\other.css", "#footer{color:#ffffff}"); + + cssBundle + .Add("/css/first.css") + .Render("/css/unprocessed_import.css"); + + Assert.AreEqual(@"@import url(""/css/other.css"");#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\unprocessed_import.css")]); + } + + [Test] + public void CanRenderCssFileWithImportStatement() + { + string importCss = + @" + @import url(""/css/other.css""); + #header { + color: #4D926F; + }"; + + + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(importCss) + .Create(); + + cssBundleFactory.FileReaderFactory.SetContents(importCss); + cssBundleFactory.FileReaderFactory.SetContentsForFile(TestUtilities.PrepareRelativePath(@"css\other.css"), "#footer{color:#ffffff}"); + + string tag = cssBundle + .Add("/css/first.css") + .ProcessImports() + .Render("/css/processed_import.css"); + + Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\processed_import.css")]); + } + + [Test] + public void CanRenderCssFileWithRelativeImportStatement() + { + string importCss = + @" + @import url(""other.css""); + #header { + color: #4D926F; + }"; + + + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(importCss) + .Create(); + + cssBundleFactory.FileReaderFactory.SetContents(importCss); + cssBundleFactory.FileReaderFactory.SetContentsForFile(TestUtilities.PrepareRelativePath(@"css\other.css"), "#footer{color:#ffffff}"); + + string tag = cssBundle + .Add("/css/first.css") + .ProcessImports() + .Render("/css/processed_import.css"); + + Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\processed_import.css")]); + } + + [Test] + public void CanRenderCssFileWithImportStatementNoQuotes() + { + string importCss = + @" + @import url(/css/other.css); + #header { + color: #4D926F; + }"; + + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(importCss) + .Create(); + + cssBundleFactory.FileReaderFactory.SetContentsForFile(TestUtilities.PrepareRelativePath(@"css\other.css"), "#footer{color:#ffffff}"); + + string tag = cssBundle + .Add("/css/first.css") + .ProcessImports() + .Render("/css/processed_import_noquotes.css"); + + Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\processed_import_noquotes.css")]); + } + + [Test] + public void CanRenderCssFileWithImportStatementSingleQuotes() + { + string importCss = + @" + @import url('/css/other.css'); + #header { + color: #4D926F; + }"; + + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(importCss) + .Create(); + + cssBundleFactory.FileReaderFactory.SetContentsForFile(TestUtilities.PrepareRelativePath(@"css\other.css"), "#footer{color:#ffffff}"); + + cssBundle + .Add("/css/first.css") + .ProcessImports() + .Render("/css/processed_import_singlequotes.css"); + + Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\processed_import_singlequotes.css")]); + } + + [Test] + public void CanRenderCssFileWithImportStatementUppercase() + { + string importCss = + @" + @IMPORT URL(/css/other.css); + #header { + color: #4D926F; + }"; + + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(importCss) + .Create(); + + cssBundleFactory.FileReaderFactory.SetContentsForFile(TestUtilities.PrepareRelativePath(@"css\other.css"), "#footer{color:#ffffff}"); + + string tag = cssBundle + .Add("/css/first.css") + .ProcessImports() + .Render("/css/processed_import_uppercase.css"); + + Assert.AreEqual("#footer{color:#fff}#header{color:#4d926f}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\processed_import_uppercase.css")]); + } + + [Test] + public void CanCreateNamedBundleWithForceRelease() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(true) + .WithContents(css) + .Create(); + + cssBundle + .Add("~/css/temp.css") + .ForceRelease() + .AsNamed("TestForce", "~/css/named_withforce.css"); + + string tag = cssBundle.RenderNamed("TestForce"); + + Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\named_withforce.css")]); + Assert.AreEqual("", tag); + } + + [Test] + public void CanBundleCssWithArbitraryAttributes() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .WithAttribute("media", "screen") + .WithAttribute("test", "other") + .Render("/css/css_with_attribute_output.css"); + + Assert.AreEqual("", tag); + } + + [Test] + public void CanBundleDebugCssWithArbitraryAttributes() + { + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(true) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("/css/first.css") + .Add("/css/second.css") + .WithAttribute("media", "screen") + .WithAttribute("test", "other") + .Render("/css/css_with_debugattribute_output.css"); + + Assert.AreEqual("\n\n", TestUtilities.NormalizeLineEndings(tag)); + } + + [Test] + public void CanCreateCachedBundle() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("~/css/temp.css") + .AsCached("TestCached", "~/static/css/TestCached.css"); + + string contents = cssBundle.RenderCached("TestCached"); + + Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", contents); + Assert.AreEqual("", tag); + } + + [Test] + public void CanCreateCachedBundleAssetTag() + { + CSSBundle cssBundle = cssBundleFactory + .WithHasher(hasher) + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + cssBundle + .Add("~/css/temp.css") + .AsCached("TestCached", "~/static/css/TestCached.css"); + + string contents = cssBundle.RenderCached("TestCached"); + cssBundle.ClearCache(); + string tag = cssBundle.RenderCachedAssetTag("TestCached"); + + Assert.AreEqual("li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}", contents); + Assert.AreEqual("", tag); + } + + [Test] + public void CanCreateCachedBundleInDebugMode() + { + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(true) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("~/css/temp.css") + .AsCached("TestCached", "~/static/css/TestCached.css"); + + Assert.AreEqual("\n", TestUtilities.NormalizeLineEndings(tag)); + } + + [Test] + public void CanBundleDirectoryContentsInDebug() { + var path = Guid.NewGuid().ToString(); + var file1 = TestUtilities.PrepareRelativePath(path + "\\file1.css"); + var file2 = TestUtilities.PrepareRelativePath(path + "\\file2.css"); + + using (new ResolverFactoryScope(typeof(SquishIt.Framework.Resolvers.FileSystemResolver).FullName, StubResolver.ForDirectory(new[] { file1, file2 }))) { + var frf = new StubFileReaderFactory(); + frf.SetContentsForFile(file1, css2); + frf.SetContentsForFile(file2, css); + + var writerFactory = new StubFileWriterFactory(); + + var tag = cssBundleFactory.WithDebuggingEnabled(true) + .WithFileReaderFactory(frf) + .WithFileWriterFactory(writerFactory) + .WithHasher(new StubHasher("hashy")) + .Create() + .Add(path) + .Render("~/output.css"); + + var expectedTag = string.Format("\n\n", path); + Assert.AreEqual(expectedTag, TestUtilities.NormalizeLineEndings(tag)); + } + } + + [Test] + public void CanBundleDirectoryContentsInDebug_Ignores_Duplicates() { + var path = Guid.NewGuid().ToString(); + var file1 = TestUtilities.PrepareRelativePath(path + "\\file1.css"); + var file2 = TestUtilities.PrepareRelativePath(path + "\\file2.css"); + + using (new ResolverFactoryScope(typeof(SquishIt.Framework.Resolvers.FileSystemResolver).FullName, StubResolver.ForDirectory(new[] { file1, file2 }))) { + var frf = new StubFileReaderFactory(); + frf.SetContentsForFile(file1, css2); + frf.SetContentsForFile(file2, css); + + var writerFactory = new StubFileWriterFactory(); + + var tag = cssBundleFactory.WithDebuggingEnabled(true) + .WithFileReaderFactory(frf) + .WithFileWriterFactory(writerFactory) + .WithHasher(new StubHasher("hashy")) + .Create() + .Add(path) + .Render("~/output.css"); + + var expectedTag = string.Format("\n\n", path); + Assert.AreEqual(expectedTag, TestUtilities.NormalizeLineEndings(tag)); + } + } + + [Test] + public void CanBundleDirectoryContentsInRelease() { + var path = Guid.NewGuid().ToString(); + var file1 = TestUtilities.PrepareRelativePath(path + "\\file1.css"); + var file2 = TestUtilities.PrepareRelativePath(path + "\\file2.css"); + + using (new ResolverFactoryScope(typeof(SquishIt.Framework.Resolvers.FileSystemResolver).FullName, StubResolver.ForDirectory(new[] { file1, file2 }))) { + var frf = new StubFileReaderFactory(); + frf.SetContentsForFile(file1, css2); + frf.SetContentsForFile(file2, css); + + var writerFactory = new StubFileWriterFactory(); + + var tag = cssBundleFactory.WithDebuggingEnabled(false) + .WithFileReaderFactory(frf) + .WithFileWriterFactory(writerFactory) + .WithHasher(new StubHasher("hashy")) + .Create() + .Add(path) + .Render("~/output.css"); + + var expectedTag = ""; + Assert.AreEqual(expectedTag, tag); + + var combined = "li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}"; + Assert.AreEqual(combined, writerFactory.Files[TestUtilities.PrepareRelativePath(@"output.css")]); + } + } + + [Test] + public void CanBundleDirectoryContentsInRelease_Ignores_Duplicates() { + var path = Guid.NewGuid().ToString(); + var file1 = TestUtilities.PrepareRelativePath(path + "\\file1.css"); + var file2 = TestUtilities.PrepareRelativePath(path + "\\file2.css"); + + using (new ResolverFactoryScope(typeof(SquishIt.Framework.Resolvers.FileSystemResolver).FullName, StubResolver.ForDirectory(new[] { file1, file2 }))) { + var frf = new StubFileReaderFactory(); + frf.SetContentsForFile(file1, css2); + frf.SetContentsForFile(file2, css); + + var writerFactory = new StubFileWriterFactory(); + + var tag = cssBundleFactory.WithDebuggingEnabled(false) + .WithFileReaderFactory(frf) + .WithFileWriterFactory(writerFactory) + .WithHasher(new StubHasher("hashy")) + .Create() + .Add(path) + .Add(file1) + .Render("~/output.css"); + + var expectedTag = ""; + Assert.AreEqual(expectedTag, tag); + + var combined = "li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}"; + Assert.AreEqual(combined, writerFactory.Files[TestUtilities.PrepareRelativePath(@"output.css")]); + } + } + + [Test] + public void CanRenderArbitraryStringsInDebug () + { + var css2Format = "{0}{1}"; + + var hrColor = "hr {color:sienna;}"; + var p = "p {margin-left:20px;}"; + + var tag = new CssBundleFactory () + .WithDebuggingEnabled (true) + .Create () + .AddString (css) + .AddString (css2Format, hrColor, p) + .Render ("doesn't matter where..."); + + var expectedTag = string.Format ("\n\n", css, string.Format (css2Format, hrColor, p)); + Assert.AreEqual (expectedTag, TestUtilities.NormalizeLineEndings (tag)); + } + + [Test] + public void CanRenderArbitraryStringsInDebugWithoutType () + { + var css2Format = "{0}{1}"; + + var hrColor = "hr {color:sienna;}"; + var p = "p {margin-left:20px;}"; + + var tag = new CssBundleFactory () + .WithDebuggingEnabled (true) + .Create () + .AddString (css) + .AddString (css2Format, hrColor, p) + .WithoutTypeAttribute () + .Render ("doesn't matter where..."); + + var expectedTag = string.Format ("\n\n", css, string.Format (css2Format, hrColor, p)); + Assert.AreEqual (expectedTag, TestUtilities.NormalizeLineEndings (tag)); + } + + [Test] + public void DoesNotRenderDuplicateArbitraryStringsInDebug () + { + var tag = new CssBundleFactory () + .WithDebuggingEnabled (true) + .Create () + .AddString (css) + .AddString (css) + .Render ("doesn't matter where..."); + + var expectedTag = string.Format ("\n", css); + Assert.AreEqual (expectedTag, TestUtilities.NormalizeLineEndings (tag)); + } + + [Test] + public void CanBundleArbitraryContentsInRelease () + { + var css2Format = "{0}{1}"; + + var hrColor = "hr {color:sienna;}"; + var p = "p {margin-left:20px;}"; + + var writerFactory = new StubFileWriterFactory (); + + var tag = new CssBundleFactory () + .WithDebuggingEnabled (false) + .WithFileWriterFactory (writerFactory) + .WithHasher (new StubHasher ("hashy")) + .Create () + .AddString (css) + .AddString (css2Format, hrColor, p) + .Render ("~/output.css"); + + var expectedTag = ""; + Assert.AreEqual (expectedTag, TestUtilities.NormalizeLineEndings (tag)); + + var minifiedScript = "li{margin-bottom:.1em;margin-left:0;margin-top:.1em}th{font-weight:normal;vertical-align:bottom}.FloatRight{float:right}.FloatLeft{float:left}hr{color:#a0522d}p{margin-left:20px}"; + Assert.AreEqual (minifiedScript, writerFactory.Files[TestUtilities.PrepareRelativePath("output.css")]); + } + + [Test] + public void PathRewritingDoesNotAffectClassesNamedUrl() + { + string css = + @" + a.url { + color: #4D926F; + } + "; + + CSSBundle cssBundle = cssBundleFactory + .WithDebuggingEnabled(false) + .WithContents(css) + .Create(); + + string tag = cssBundle + .Add("~/css/something/test.css") + .Render("~/css/output_rewriting_url.css"); + + string contents = cssBundleFactory.FileWriterFactory.Files[TestUtilities.PrepareRelativePath(@"css\output_rewriting_url.css")]; + + Assert.AreEqual("a.url{color:#4d926f}", contents); + } + } } \ No newline at end of file From 928950c94d4ea3c697b703b8c0d39fc3aeddda49 Mon Sep 17 00:00:00 2001 From: LaazyJ Date: Sun, 26 Feb 2012 11:59:02 +0000 Subject: [PATCH 2/5] Generic methods for specifying any minifier types I left the helper methods for specific minifiers because it makes the available types for discoverable while still allowing user to specify their own type using the generic method. --- SquishIt.Framework/Configuration.cs | 56 ++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/SquishIt.Framework/Configuration.cs b/SquishIt.Framework/Configuration.cs index fb16f3c..f744c9b 100644 --- a/SquishIt.Framework/Configuration.cs +++ b/SquishIt.Framework/Configuration.cs @@ -9,13 +9,44 @@ namespace SquishIt.Framework { public class Configuration { + public Configuration UseMinifierForCss() + where TMinifier : IMinifier + { + return UseMinifierForCss(typeof (TMinifier)); + } + + public Configuration UseMinifierForCss(Type minifierType) + { + if (!typeof(IMinifier).IsAssignableFrom(minifierType)) + throw new InvalidCastException( + String.Format("Type '{0}' must implement '{1}' to be used for Css minification.", + minifierType, typeof (IMinifier))); + _defaultCssMinifier = minifierType; + return this; + } + + public Configuration UseMinifierForJs() + where TMinifier : IMinifier + { + return UseMinifierForJs(typeof (TMinifier)); + } + + public Configuration UseMinifierForJs(Type minifierType) + { + if (!typeof(IMinifier).IsAssignableFrom(minifierType)) + throw new InvalidCastException( + String.Format("Type '{0}' must implement '{1}' to be used for Javascript minification.", + minifierType, typeof (IMinifier))); + _defaultJsMinifier = minifierType; + return this; + } + /// /// Use Yahoo YUI Compressor for CSS minification by default. /// public Configuration UseYuiForCssMinification() { - _defaultCssMinifier = typeof (YuiCompressor); - return this; + return UseMinifierForCss(); } /// @@ -23,8 +54,7 @@ public Configuration UseYuiForCssMinification() /// public Configuration UseMsAjaxForCssMinification() { - _defaultCssMinifier = typeof (MsCompressor); - return this; + return UseMinifierForCss(); } /// @@ -32,8 +62,7 @@ public Configuration UseMsAjaxForCssMinification() /// public Configuration UseNoCssMinification() { - _defaultCssMinifier = typeof (NullCompressor); - return this; + return UseMinifierForCss(); } /// @@ -41,8 +70,7 @@ public Configuration UseNoCssMinification() /// public Configuration UseMsAjaxForJsMinification() { - _defaultJsMinifier = typeof (MsMinifier); - return this; + return UseMinifierForJs(); } /// @@ -50,8 +78,7 @@ public Configuration UseMsAjaxForJsMinification() /// public Configuration UseYuiForJsMinification() { - _defaultJsMinifier = typeof (YuiMinifier); - return this; + return UseMinifierForJs(); } /// @@ -59,8 +86,7 @@ public Configuration UseYuiForJsMinification() /// public Configuration UseClosureForMinification() { - _defaultJsMinifier = typeof (ClosureMinifier); - return this; + return UseMinifierForJs(); } /// @@ -68,8 +94,7 @@ public Configuration UseClosureForMinification() /// public Configuration UseNoJsMinification() { - _defaultJsMinifier = typeof (NullMinifier); - return this; + return UseMinifierForJs(); } /// @@ -77,8 +102,7 @@ public Configuration UseNoJsMinification() /// public Configuration UseJsMinForJsMinification() { - _defaultJsMinifier = typeof (JsMinMinifier); - return this; + return UseMinifierForJs(); } static Type _defaultCssMinifier = typeof (MsCompressor); From 48f2744fb682ce1aa88686afa4df6e7c28d28389 Mon Sep 17 00:00:00 2001 From: Shawn Wildermuth Date: Tue, 28 Feb 2012 17:58:40 -0500 Subject: [PATCH 3/5] Added Support for AddDirectory with optional recursiveness. --- SquishIt.Framework/Base/Asset.cs | 4 +++- SquishIt.Framework/Base/BundleBase.cs | 18 ++++++++++++------ SquishIt.Framework/Files/Input.cs | 6 ++++-- .../Resolvers/EmbeddedResourceResolver.cs | 3 ++- .../Resolvers/FileSystemResolver.cs | 4 ++-- SquishIt.Framework/Resolvers/HttpResolver.cs | 2 +- SquishIt.Framework/Resolvers/IResolver.cs | 2 +- SquishIt.Tests/CssBundleTests.cs | 3 ++- SquishIt.Tests/FileSystemResolverTests.cs | 4 ++-- SquishIt.Tests/Stubs/StubResolver.cs | 2 +- SquishItAspNetTest/Default.aspx | 8 +++++++- 11 files changed, 37 insertions(+), 19 deletions(-) diff --git a/SquishIt.Framework/Base/Asset.cs b/SquishIt.Framework/Base/Asset.cs index 29cad83..4f41b3c 100644 --- a/SquishIt.Framework/Base/Asset.cs +++ b/SquishIt.Framework/Base/Asset.cs @@ -9,6 +9,7 @@ internal class Asset internal int Order { get; set; } internal bool IsEmbeddedResource { get; set; } internal bool DownloadRemote { get; set; } + internal bool IsRecursive { get; set; } internal bool IsLocal { @@ -34,12 +35,13 @@ internal Asset() { } - internal Asset(string localPath, string remotePath = null, int order = 0, bool isEmbeddedResource = false) + internal Asset(string localPath, string remotePath = null, int order = 0, bool isEmbeddedResource = false, bool isRecursive = true) { LocalPath = localPath; RemotePath = remotePath; Order = order; IsEmbeddedResource = isEmbeddedResource; + IsRecursive = isRecursive; } } } \ No newline at end of file diff --git a/SquishIt.Framework/Base/BundleBase.cs b/SquishIt.Framework/Base/BundleBase.cs index 4c48184..9c8157a 100644 --- a/SquishIt.Framework/Base/BundleBase.cs +++ b/SquishIt.Framework/Base/BundleBase.cs @@ -80,7 +80,7 @@ private Input GetInputFile(Asset asset) { if (debugStatusReader.IsDebuggingEnabled()) { - return GetFileSystemPath(asset.LocalPath); + return GetFileSystemPath(asset.LocalPath, asset.IsRecursive); } if (asset.IsRemoteDownload) @@ -89,7 +89,7 @@ private Input GetInputFile(Asset asset) } else { - return GetFileSystemPath(asset.LocalPath); + return GetFileSystemPath(asset.LocalPath, asset.IsRecursive); } } else @@ -108,20 +108,20 @@ private List GetInputFiles(List assets) return inputFiles; } - private Input GetFileSystemPath(string localPath) + private Input GetFileSystemPath(string localPath, bool isRecursive = true) { string mappedPath = FileSystem.ResolveAppRelativePathToFileSystem(localPath); - return new Input(mappedPath, ResolverFactory.Get()); + return new Input(mappedPath, isRecursive, ResolverFactory.Get()); } private Input GetHttpPath(string remotePath) { - return new Input(remotePath, ResolverFactory.Get()); + return new Input(remotePath, false, ResolverFactory.Get()); } private Input GetEmbeddedResourcePath(string resourcePath) { - return new Input(resourcePath, ResolverFactory.Get()); + return new Input(resourcePath, false, ResolverFactory.Get()); } private string ExpandAppRelativePath(string file) @@ -198,6 +198,12 @@ public T Add(string fileOrFolderPath) return (T)this; } + public T AddDirectory(string folderPath, bool recursive = true) + { + AddAsset(new Asset(folderPath, isRecursive: recursive)); + return (T)this; + } + public T AddString(string content) { arbitrary.Add(content); diff --git a/SquishIt.Framework/Files/Input.cs b/SquishIt.Framework/Files/Input.cs index 59755de..4ce0d24 100644 --- a/SquishIt.Framework/Files/Input.cs +++ b/SquishIt.Framework/Files/Input.cs @@ -6,10 +6,12 @@ public class Input { public string Path { get; private set; } public Resolvers.IResolver Resolver { get; private set; } + public bool IsRecursive { get; private set; } - public Input(string filePath, Resolvers.IResolver resolver) + public Input(string filePath, bool recursive, Resolvers.IResolver resolver) { Path = filePath; + IsRecursive = recursive; Resolver = resolver; } @@ -21,7 +23,7 @@ public IEnumerable TryResolve(IEnumerable allowedExtensions) { if (IsDirectory) { - return Resolver.TryResolveFolder(Path, allowedExtensions); + return Resolver.TryResolveFolder(Path, IsRecursive, allowedExtensions); } else { diff --git a/SquishIt.Framework/Resolvers/EmbeddedResourceResolver.cs b/SquishIt.Framework/Resolvers/EmbeddedResourceResolver.cs index 7c51bfa..af8eff4 100644 --- a/SquishIt.Framework/Resolvers/EmbeddedResourceResolver.cs +++ b/SquishIt.Framework/Resolvers/EmbeddedResourceResolver.cs @@ -32,7 +32,8 @@ public string TryResolve(string file) } } - public IEnumerable TryResolveFolder(string path, IEnumerable allowedExtensions) { + public IEnumerable TryResolveFolder(string path, bool recursive, IEnumerable allowedExtensions) + { throw new NotImplementedException("Adding entire directories only supported by FileSystemResolver."); } diff --git a/SquishIt.Framework/Resolvers/FileSystemResolver.cs b/SquishIt.Framework/Resolvers/FileSystemResolver.cs index 6a917a5..c8045ab 100644 --- a/SquishIt.Framework/Resolvers/FileSystemResolver.cs +++ b/SquishIt.Framework/Resolvers/FileSystemResolver.cs @@ -17,11 +17,11 @@ public bool IsDirectory(string path) return Directory.Exists(path); } - public IEnumerable TryResolveFolder(string path, IEnumerable allowedFileExtensions) + public IEnumerable TryResolveFolder(string path, bool recursive, IEnumerable allowedFileExtensions) { if (IsDirectory(path)) { - var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories) + var files = Directory.GetFiles(path, "*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) .Where( f => allowedFileExtensions == null || allowedFileExtensions.Any(x => f.EndsWith(x, StringComparison.InvariantCultureIgnoreCase))) .ToArray(); diff --git a/SquishIt.Framework/Resolvers/HttpResolver.cs b/SquishIt.Framework/Resolvers/HttpResolver.cs index fe162cb..531b5f4 100644 --- a/SquishIt.Framework/Resolvers/HttpResolver.cs +++ b/SquishIt.Framework/Resolvers/HttpResolver.cs @@ -32,7 +32,7 @@ public string TryResolve(string file) } } - public IEnumerable TryResolveFolder(string path, IEnumerable allowedExtensions) { + public IEnumerable TryResolveFolder(string path, bool recursive, IEnumerable allowedExtensions) { throw new NotImplementedException("Adding entire directories only supported by FileSystemResolver."); } diff --git a/SquishIt.Framework/Resolvers/IResolver.cs b/SquishIt.Framework/Resolvers/IResolver.cs index ead555b..8590bf0 100644 --- a/SquishIt.Framework/Resolvers/IResolver.cs +++ b/SquishIt.Framework/Resolvers/IResolver.cs @@ -6,6 +6,6 @@ public interface IResolver { bool IsDirectory(string path); string TryResolve(string path); - IEnumerable TryResolveFolder(string path, IEnumerable allowedExtensions); + IEnumerable TryResolveFolder(string path, bool recursive, IEnumerable allowedExtensions); } } \ No newline at end of file diff --git a/SquishIt.Tests/CssBundleTests.cs b/SquishIt.Tests/CssBundleTests.cs index 5b8ab7f..cde7ecb 100644 --- a/SquishIt.Tests/CssBundleTests.cs +++ b/SquishIt.Tests/CssBundleTests.cs @@ -78,7 +78,8 @@ public void CanAddMultiplePathFiles() .WithDebuggingEnabled(true) .Create(); - cssBundle1.Add("/css/first.css", "/css/second.css"); + // Obsolete Test + // cssBundle1.Add("/css/first.css", "/css/second.css"); cssBundle2.Add("/css/first.css").Add("/css/second.css"); var cssBundle1Assets = cssBundle1.bundleState.Assets; diff --git a/SquishIt.Tests/FileSystemResolverTests.cs b/SquishIt.Tests/FileSystemResolverTests.cs index e90178d..83dd57f 100644 --- a/SquishIt.Tests/FileSystemResolverTests.cs +++ b/SquishIt.Tests/FileSystemResolverTests.cs @@ -70,7 +70,7 @@ public void CanResolveDirectory() File.Create(Path.Combine(directory.FullName, "file1")).Close(); File.Create(Path.Combine(directory.FullName, "file2")).Close(); - var result = new FileSystemResolver().TryResolveFolder(path, null).ToList(); + var result = new FileSystemResolver().TryResolveFolder(path, true, null).ToList(); Assert.AreEqual(2, result.Count); Assert.Contains(path + Path.DirectorySeparatorChar + "file1", result); Assert.Contains(path + Path.DirectorySeparatorChar + "file2", result); @@ -93,7 +93,7 @@ public void CanResolveDirectory_Filters_Files_By_Extension() File.Create(Path.Combine(directory.FullName, "file2.css")).Close(); File.Create(Path.Combine(directory.FullName, "file21.JS")).Close(); - var result = new FileSystemResolver().TryResolveFolder(path, new[] { ".js" }).ToList(); + var result = new FileSystemResolver().TryResolveFolder(path, true, new[] { ".js" }).ToList(); Assert.AreEqual(2, result.Count); Assert.Contains(path + Path.DirectorySeparatorChar + "file1.js", result); Assert.Contains(path + Path.DirectorySeparatorChar + "file21.JS", result); diff --git a/SquishIt.Tests/Stubs/StubResolver.cs b/SquishIt.Tests/Stubs/StubResolver.cs index 745e913..0dbc044 100644 --- a/SquishIt.Tests/Stubs/StubResolver.cs +++ b/SquishIt.Tests/Stubs/StubResolver.cs @@ -20,7 +20,7 @@ public string TryResolve(string file) return _pathToResolveTo; } - public IEnumerable TryResolveFolder(string path, IEnumerable allowedExtensions) { + public IEnumerable TryResolveFolder(string path, bool recursive, IEnumerable allowedExtensions) { return _directoryContents .Where(dc => allowedExtensions.Any(ext => dc.EndsWith(ext, System.StringComparison.InvariantCultureIgnoreCase))) .ToArray(); diff --git a/SquishItAspNetTest/Default.aspx b/SquishItAspNetTest/Default.aspx index 2589034..680d50e 100644 --- a/SquishItAspNetTest/Default.aspx +++ b/SquishItAspNetTest/Default.aspx @@ -55,7 +55,13 @@ .Add("~/css/import.css") .WithAttribute("media", "screen") .ForceRelease() - .Render("~/combinedimport_#.css") %> + .Render("~/combinedimport_#.css") %> + + <%= Bundle.Css() + .AddDirectory("~/css/", true) + .WithAttribute("media", "screen") + .ForceDebug() + .Render("~/nonrecursivedirectory_#.css") %>
From b694cd54b21920d55a764493341e015824051da4 Mon Sep 17 00:00:00 2001 From: Alex Ullrich Date: Tue, 28 Feb 2012 19:43:03 -0500 Subject: [PATCH 4/5] fixing CanAddMultiplePathFiles test to compare assets from both bundles --- SquishIt.Tests/CssBundleTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SquishIt.Tests/CssBundleTests.cs b/SquishIt.Tests/CssBundleTests.cs index 80b582d..829dc19 100644 --- a/SquishIt.Tests/CssBundleTests.cs +++ b/SquishIt.Tests/CssBundleTests.cs @@ -79,11 +79,11 @@ public void CanAddMultiplePathFiles() .Create(); // Obsolete Test - // cssBundle1.Add("/css/first.css", "/css/second.css"); + cssBundle1.Add("/css/first.css", "/css/second.css"); cssBundle2.Add("/css/first.css").Add("/css/second.css"); var cssBundle1Assets = cssBundle1.bundleState.Assets; - var cssBundle2Assets = cssBundle1.bundleState.Assets; + var cssBundle2Assets = cssBundle2.bundleState.Assets; Assert.AreEqual(cssBundle1Assets.Count, cssBundle2Assets.Count); for (var i = 0; i < cssBundle1Assets.Count; i++) From e7d5c7f17ed66aaff58e3679f1af12492b0130c4 Mon Sep 17 00:00:00 2001 From: Alex Ullrich Date: Tue, 27 Mar 2012 23:21:20 -0400 Subject: [PATCH 5/5] replacing hash set w/ list in bundle base (fix #167) --- SquishIt.Framework/Base/BundleBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SquishIt.Framework/Base/BundleBase.cs b/SquishIt.Framework/Base/BundleBase.cs index c10271b..8400949 100644 --- a/SquishIt.Framework/Base/BundleBase.cs +++ b/SquishIt.Framework/Base/BundleBase.cs @@ -206,7 +206,8 @@ public T AddDirectory(string folderPath, bool recursive = true) public T AddString(string content) { - arbitrary.Add(content); + if(!arbitrary.Contains(content)) + arbitrary.Add(content); return (T)this; }