diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/AttributeMatcher.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/AttributeMatcher.cs
index 7608782758..331cffc9cf 100644
--- a/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/AttributeMatcher.cs
+++ b/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/AttributeMatcher.cs
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Internal;
-using Microsoft.Framework.Logging;
namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
{
@@ -15,44 +14,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
///
public static class AttributeMatcher
{
- ///
- /// Determines whether a 's required attributes are present, non null, non empty, and
- /// non whitepsace.
- ///
- /// The .
- /// The .
- ///
- /// The attributes the requires in order to run.
- ///
- /// An optional to log warning details to.
- /// A indicating whether the should run.
- public static bool AllRequiredAttributesArePresent(
- [NotNull] TagHelperContext tagHelperContext,
- [NotNull] ViewContext viewContext,
- [NotNull] IEnumerable requiredAttributes,
- ILogger logger)
- {
- var attributes = GetPresentMissingAttributes(tagHelperContext, requiredAttributes);
-
- if (attributes.Missing.Any())
- {
- if (attributes.Present.Any() && logger != null && logger.IsEnabled(LogLevel.Warning))
- {
- // At least 1 attribute was present indicating the user intended to use the tag helper,
- // but at least 1 was missing too, so log a warning with the details.
- logger.WriteWarning(new MissingAttributeLoggerStructure(
- tagHelperContext.UniqueId,
- viewContext.View.Path,
- attributes.Missing));
- }
-
- return false;
- }
-
- // All required attributes present
- return true;
- }
-
///
/// Determines the modes a can run in based on which modes have all their required
/// attributes present, non null, non empty, and non whitepsace.
diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/MissingAttributeLoggerStructure.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/MissingAttributeLoggerStructure.cs
deleted file mode 100644
index 988741f583..0000000000
--- a/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/MissingAttributeLoggerStructure.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Generic;
-using Microsoft.AspNet.Razor.Runtime.TagHelpers;
-using Microsoft.Framework.Logging;
-
-namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
-{
- ///
- /// An for log messages regarding instances that opt out of
- /// processing due to missing required attributes.
- ///
- public class MissingAttributeLoggerStructure : ILoggerStructure
- {
- private readonly string _uniqueId;
- private readonly string _viewPath;
- private readonly IEnumerable> _values;
-
- // Internal for unit testing
- internal IEnumerable MissingAttributes { get; }
-
- ///
- /// Creates a new .
- ///
- /// The unique ID of the HTML element this message applies to.
- /// The path to the view.
- /// The missing required attributes.
- public MissingAttributeLoggerStructure(string uniqueId, string viewPath, IEnumerable missingAttributes)
- {
- _uniqueId = uniqueId;
- _viewPath = viewPath;
- MissingAttributes = missingAttributes;
- _values = new Dictionary
- {
- ["UniqueId"] = _uniqueId,
- ["ViewPath"] = _viewPath,
- ["MissingAttributes"] = MissingAttributes
- };
- }
-
- ///
- /// The log message.
- ///
- public string Message
- {
- get
- {
- return "Tag Helper has one or more missing required attributes.";
- }
- }
-
- ///
- /// Gets the values associated with this structured log message.
- ///
- /// The values.
- public IEnumerable> GetValues()
- {
- return _values;
- }
-
- ///
- /// Generates a human readable string for this structured log message.
- ///
- /// The message.
- public string Format()
- {
- return string.Format("Tag Helper with ID {0} in view '{1}' is missing attributes: {2}",
- _uniqueId,
- _viewPath,
- string.Join(",", MissingAttributes));
- }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/PartialAttributeLoggerStructure.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/PartialAttributeLoggerStructureOfT.cs
similarity index 72%
rename from src/Microsoft.AspNet.Mvc.TagHelpers/Internal/PartialAttributeLoggerStructure.cs
rename to src/Microsoft.AspNet.Mvc.TagHelpers/Internal/PartialAttributeLoggerStructureOfT.cs
index 12f1cd8f05..fd7d0c1075 100644
--- a/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/PartialAttributeLoggerStructure.cs
+++ b/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/PartialAttributeLoggerStructureOfT.cs
@@ -14,12 +14,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
/// An for log messages regarding instances that opt out of
/// processing due to missing attributes for one of several possible modes.
///
- public class PartialModeMatchLoggerStructure : ILoggerStructure
+ public class PartialModeMatchLoggerStructure : PartialModeMatchLoggerStructure
{
private readonly string _uniqueId;
private readonly string _viewPath;
private readonly IEnumerable> _partialMatches;
- private readonly IEnumerable> _values;
///
/// Creates a new .
@@ -31,43 +30,23 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
string uniqueId,
string viewPath,
[NotNull] IEnumerable> partialMatches)
+ : base(values: new Dictionary
+ {
+ ["UniqueId"] = uniqueId,
+ ["ViewPath"] = viewPath,
+ ["PartialMatches"] = partialMatches
+ })
{
_uniqueId = uniqueId;
_viewPath = viewPath;
_partialMatches = partialMatches;
- _values = new Dictionary
- {
- ["UniqueId"] = _uniqueId,
- ["ViewPath"] = _viewPath,
- ["PartialMatches"] = partialMatches
- };
}
-
- ///
- /// The log message.
- ///
- public string Message
- {
- get
- {
- return "Tag Helper has missing required attributes.";
- }
- }
-
- ///
- /// Gets the values associated with this structured log message.
- ///
- /// The values.
- public IEnumerable> GetValues()
- {
- return _values;
- }
-
+
///
/// Generates a human readable string for this structured log message.
///
/// The message.
- public string Format()
+ public override string Format()
{
var newLine = Environment.NewLine;
return string.Format(
diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/PartialModeMatchLoggerStructure.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/PartialModeMatchLoggerStructure.cs
new file mode 100644
index 0000000000..f5759e4539
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.TagHelpers/Internal/PartialModeMatchLoggerStructure.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Microsoft.AspNet.Razor.Runtime.TagHelpers;
+using Microsoft.Framework.Logging;
+
+namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
+{
+ ///
+ /// An for log messages regarding instances that opt out of
+ /// processing due to missing attributes for one of several possible modes.
+ ///
+ public abstract class PartialModeMatchLoggerStructure : ILoggerStructure
+ {
+ private readonly IEnumerable> _values;
+
+ protected PartialModeMatchLoggerStructure(IEnumerable> values)
+ {
+ _values = values;
+ }
+
+ ///
+ /// The log message.
+ ///
+ public string Message
+ {
+ get
+ {
+ return "Tag Helper has missing required attributes.";
+ }
+ }
+
+ ///
+ /// Returns a human-readable string of the structured data.
+ ///
+ public abstract string Format();
+
+ ///
+ /// Gets the values associated with this structured log message.
+ ///
+ /// The values.
+ public IEnumerable> GetValues()
+ {
+ return _values;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/LinkTagHelper.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/LinkTagHelper.cs
index 91cc7707cb..f130e075e1 100644
--- a/src/Microsoft.AspNet.Mvc.TagHelpers/LinkTagHelper.cs
+++ b/src/Microsoft.AspNet.Mvc.TagHelpers/LinkTagHelper.cs
@@ -66,15 +66,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
private enum Mode
{
- ///
- /// Rendering a fallback block if primary stylesheet fails to load. Will also do globbing if the appropriate
- /// properties are set.
- ///
- Fallback,
///
/// Just performing file globbing search for the href, rendering a separate <link> for each match.
///
- GlobbedHref
+ GlobbedHref = 0,
+ ///
+ /// Rendering a fallback block if primary stylesheet fails to load. Will also do globbing for both the
+ /// primary and fallback hrefs if the appropriate properties are set.
+ ///
+ Fallback = 1,
}
///
@@ -93,15 +93,14 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
public string HrefExclude { get; set; }
///
- /// The URL of a CSS stylesheet to fallback to in the case the primary one fails (as specified in the href
- /// attribute).
+ /// The URL of a CSS stylesheet to fallback to in the case the primary one fails.
///
[HtmlAttributeName(FallbackHrefAttributeName)]
public string FallbackHref { get; set; }
///
/// A comma separated list of globbed file patterns of CSS stylesheets to fallback to in the case the primary
- /// one fails (as specified in the href attribute).
+ /// one fails.
/// The glob patterns are assessed relative to the application's 'webroot' setting.
///
[HtmlAttributeName(FallbackHrefIncludeAttributeName)]
@@ -109,7 +108,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
///
/// A comma separated list of globbed file patterns of CSS stylesheets to exclude from the fallback list, in
- /// the case the primary one fails (as specified in the href attribute).
+ /// the case the primary one fails.
/// The glob patterns are assessed relative to the application's 'webroot' setting.
/// Must be used in conjunction with .
///
@@ -161,9 +160,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
var modeResult = AttributeMatcher.DetermineMode(context, ModeDetails);
- Debug.Assert(modeResult.FullMatches.Select(match => match.Mode).Distinct().Count() <= 1,
- $"There should only be one mode match, check the {nameof(ModeDetails)}");
-
modeResult.LogDetails(Logger, this, context.UniqueId, ViewContext.View.Path);
if (!modeResult.FullMatches.Any())
@@ -172,7 +168,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return;
}
- var mode = modeResult.FullMatches.First().Mode;
+ // Get the highest matched mode
+ var mode = modeResult.FullMatches.Select(match => match.Mode).Max();
// NOTE: Values in TagHelperOutput.Attributes are already HtmlEncoded
var attributes = new Dictionary(output.Attributes);
@@ -206,11 +203,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
attributes.TryGetValue("href", out staticHref);
EnsureGlobbingUrlBuilder();
- var hrefs = GlobbingUrlBuilder.BuildUrlList(staticHref, HrefInclude, HrefExclude);
+ var urls = GlobbingUrlBuilder.BuildUrlList(staticHref, HrefInclude, HrefExclude);
- foreach (var href in hrefs)
+ foreach (var url in urls)
{
- attributes["href"] = WebUtility.HtmlEncode(href);
+ attributes["href"] = WebUtility.HtmlEncode(url);
BuildLinkTag(attributes, builder);
}
}
diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/ScriptTagHelper.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/ScriptTagHelper.cs
index 975209caba..fe2017d7b2 100644
--- a/src/Microsoft.AspNet.Mvc.TagHelpers/ScriptTagHelper.cs
+++ b/src/Microsoft.AspNet.Mvc.TagHelpers/ScriptTagHelper.cs
@@ -2,12 +2,17 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using System.Globalization;
+using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
+using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Mvc.TagHelpers.Internal;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
+using Microsoft.Framework.Cache.Memory;
using Microsoft.Framework.Logging;
using Microsoft.Framework.WebEncoders;
@@ -22,24 +27,92 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
///
public class ScriptTagHelper : TagHelper
{
+ private const string SrcIncludeAttributeName = "asp-src-include";
+ private const string SrcExcludeAttributeName = "asp-src-exclude";
private const string FallbackSrcAttributeName = "asp-fallback-src";
+ private const string FallbackSrcIncludeAttributeName = "asp-fallback-src-include";
+ private const string FallbackSrcExcludeAttributeName = "asp-fallback-src-exclude";
private const string FallbackTestExpressionAttributeName = "asp-fallback-test";
private const string SrcAttributeName = "src";
- // NOTE: All attributes are required for the ScriptTagHelper to process.
- private static readonly string[] RequiredAttributes = new[]
- {
- FallbackSrcAttributeName,
- FallbackTestExpressionAttributeName,
+ private static readonly ModeAttributes[] ModeDetails = new[] {
+ // Globbed src (include only)
+ ModeAttributes.Create(Mode.GlobbedSrc, new [] { SrcIncludeAttributeName }),
+ // Globbed src (include & exclude)
+ ModeAttributes.Create(Mode.GlobbedSrc, new [] { SrcIncludeAttributeName, SrcExcludeAttributeName }),
+ // Fallback with static src
+ ModeAttributes.Create(
+ Mode.Fallback, new[]
+ {
+ FallbackSrcAttributeName,
+ FallbackTestExpressionAttributeName
+ }),
+ // Fallback with globbed src (include only)
+ ModeAttributes.Create(
+ Mode.Fallback, new[] {
+ FallbackSrcIncludeAttributeName,
+ FallbackTestExpressionAttributeName
+ }),
+ // Fallback with globbed src (include & exclude)
+ ModeAttributes.Create(
+ Mode.Fallback, new[] {
+ FallbackSrcIncludeAttributeName,
+ FallbackSrcExcludeAttributeName,
+ FallbackTestExpressionAttributeName
+ }),
};
+ private enum Mode
+ {
+ ///
+ /// Just performing file globbing search for the src, rendering a separate <script> for each match.
+ ///
+ GlobbedSrc = 0,
+ ///
+ /// Rendering a fallback block if primary javascript fails to load. Will also do globbing for both the
+ /// primary and fallback srcs if the appropriate properties are set.
+ ///
+ Fallback = 1
+ }
+
///
- /// The URL of a Script tag to fallback to in the case the primary one fails (as specified in the src
- /// attribute).
+ /// A comma separated list of globbed file patterns of JavaScript scripts to load.
+ /// The glob patterns are assessed relative to the application's 'webroot' setting.
+ ///
+ [HtmlAttributeName(SrcIncludeAttributeName)]
+ public string SrcInclude { get; set; }
+
+ ///
+ /// A comma separated list of globbed file patterns of JavaScript scripts to exclude from loading.
+ /// The glob patterns are assessed relative to the application's 'webroot' setting.
+ /// Must be used in conjunction with .
+ ///
+ [HtmlAttributeName(SrcExcludeAttributeName)]
+ public string SrcExclude { get; set; }
+
+ ///
+ /// The URL of a Script tag to fallback to in the case the primary one fails.
///
[HtmlAttributeName(FallbackSrcAttributeName)]
public string FallbackSrc { get; set; }
+ ///
+ /// A comma separated list of globbed file patterns of JavaScript scripts to fallback to in the case the
+ /// primary one fails.
+ /// The glob patterns are assessed relative to the application's 'webroot' setting.
+ ///
+ [HtmlAttributeName(FallbackSrcIncludeAttributeName)]
+ public string FallbackSrcInclude { get; set; }
+
+ ///
+ /// A comma separated list of globbed file patterns of JavaScript scripts to exclude from the fallback list, in
+ /// the case the primary one fails.
+ /// The glob patterns are assessed relative to the application's 'webroot' setting.
+ /// Must be used in conjunction with .
+ ///
+ [HtmlAttributeName(FallbackSrcExcludeAttributeName)]
+ public string FallbackSrcExclude { get; set; }
+
///
/// The script method defined in the primary script to use for the fallback test.
///
@@ -50,84 +123,167 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
[Activate]
protected internal ILogger Logger { get; set; }
+ [Activate]
+ protected internal IHostingEnvironment HostingEnvironment { get; set; }
+
[Activate]
protected internal ViewContext ViewContext { get; set; }
+ [Activate]
+ protected internal IMemoryCache Cache { get; set; }
+
+ // Internal for ease of use when testing.
+ protected internal GlobbingUrlBuilder GlobbingUrlBuilder { get; set; }
+
///
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
- if (!AttributeMatcher.AllRequiredAttributesArePresent(context, ViewContext, RequiredAttributes, Logger))
- {
- if (Logger.IsEnabled(LogLevel.Verbose))
- {
- Logger.WriteVerbose(
- "Skipping processing for {0} with ID {1} on view '{2}'",
- nameof(ScriptTagHelper),
- context.UniqueId,
- ViewContext.View.Path);
- }
+ var modeResult = AttributeMatcher.DetermineMode(context, ModeDetails);
+ modeResult.LogDetails(Logger, this, context.UniqueId, ViewContext.View.Path);
+
+ if (!modeResult.FullMatches.Any())
+ {
+ // No attributes matched so we have nothing to do
return;
}
- var content = new StringBuilder();
-
- // NOTE: Values in Output.Attributes are already HtmlEncoded
-
- // We've taken over rendering here so prevent the element rendering the outer tag
- output.TagName = null;
-
- // Rebuild the tag.
- content.Append("");
- // Build the ");
-
- output.Content = content.ToString();
+ // We've taken over tag rendering, so prevent rendering the outer tag
+ output.TagName = null;
+ output.Content = builder.ToString();
}
- private void AppendSrc(StringBuilder content, string srcKey)
+ private void BuildGlobbedScriptTags(
+ string originalContent,
+ IDictionary attributes,
+ StringBuilder builder)
+ {
+ // Build a ");
+ }
+ }
+
+ private void EnsureGlobbingUrlBuilder()
+ {
+ if (GlobbingUrlBuilder == null)
+ {
+ GlobbingUrlBuilder = new GlobbingUrlBuilder(
+ HostingEnvironment.WebRootFileProvider,
+ Cache,
+ ViewContext.HttpContext.Request.PathBase);
+ }
+ }
+
+ private static void BuildScriptTag(
+ string content,
+ IDictionary attributes,
+ StringBuilder builder)
+ {
+ builder.Append("");
+ }
+
+ private void AppendSrc(StringBuilder content, string srcKey, string srcValue)
{
// Append src attribute in the original place and replace the content the fallback content
// No need to encode the key because we know it is "src".
content.Append(" ")
.Append(srcKey)
.Append("=\\\"")
- .Append(WebUtility.HtmlEncode(FallbackSrc))
+ .Append(WebUtility.HtmlEncode(srcValue))
.Append("\\\"");
}
}
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/Compiler/Resources/MvcTagHelpersWebSite.MvcTagHelper_Home.Link.html b/test/Microsoft.AspNet.Mvc.FunctionalTests/Compiler/Resources/MvcTagHelpersWebSite.MvcTagHelper_Home.Link.html
index 76fee781c2..85f40d625f 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/Compiler/Resources/MvcTagHelpersWebSite.MvcTagHelper_Home.Link.html
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/Compiler/Resources/MvcTagHelpersWebSite.MvcTagHelper_Home.Link.html
@@ -39,6 +39,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -59,6 +75,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/Compiler/Resources/MvcTagHelpersWebSite.MvcTagHelper_Home.Script.html b/test/Microsoft.AspNet.Mvc.FunctionalTests/Compiler/Resources/MvcTagHelpersWebSite.MvcTagHelper_Home.Script.html
index a09eef5de3..70102a9ac7 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/Compiler/Resources/MvcTagHelpersWebSite.MvcTagHelper_Home.Script.html
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/Compiler/Resources/MvcTagHelpersWebSite.MvcTagHelper_Home.Script.html
@@ -1,4 +1,5 @@
-
+
+
@@ -6,22 +7,90 @@
Script tag helper test
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/LinkTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/LinkTagHelperTest.cs
index eb405d9dd5..cea73a981b 100644
--- a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/LinkTagHelperTest.cs
+++ b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/LinkTagHelperTest.cs
@@ -351,20 +351,16 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return new TagHelperOutput(tagName, attributes);
}
- private static IHostingEnvironment MakeHostingEnvironment(IFileProvider webRootFileProvider = null)
+ private static IHostingEnvironment MakeHostingEnvironment()
{
var emptyDirectoryContents = new Mock();
emptyDirectoryContents.Setup(dc => dc.GetEnumerator())
.Returns(Enumerable.Empty().GetEnumerator());
- if (webRootFileProvider == null)
- {
- var mockFileProvider = new Mock();
- mockFileProvider.Setup(fp => fp.GetDirectoryContents(It.IsAny()))
- .Returns(emptyDirectoryContents.Object);
- webRootFileProvider = mockFileProvider.Object;
- }
+ var mockFileProvider = new Mock();
+ mockFileProvider.Setup(fp => fp.GetDirectoryContents(It.IsAny()))
+ .Returns(emptyDirectoryContents.Object);
var hostingEnvironment = new Mock();
- hostingEnvironment.Setup(h => h.WebRootFileProvider).Returns(webRootFileProvider);
+ hostingEnvironment.Setup(h => h.WebRootFileProvider).Returns(mockFileProvider.Object);
return hostingEnvironment.Object;
}
diff --git a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ScriptTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ScriptTagHelperTest.cs
index ce0d41f4e0..35461a3d80 100644
--- a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ScriptTagHelperTest.cs
+++ b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ScriptTagHelperTest.cs
@@ -6,6 +6,8 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using Microsoft.AspNet.FileProviders;
+using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
@@ -20,39 +22,112 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
public class ScriptTagHelperTest
{
+ public static TheoryData RunsWhenRequiredAttributesArePresent_Data
+ {
+ get
+ {
+ return new TheoryData, Action>
+ {
+ {
+ new Dictionary
+ {
+ ["asp-src-include"] = "*.js"
+ },
+ tagHelper =>
+ {
+ tagHelper.SrcInclude = "*.js";
+ }
+ },
+ {
+ new Dictionary
+ {
+ ["asp-src-include"] = "*.js",
+ ["asp-src-exclude"] = "*.min.js"
+ },
+ tagHelper =>
+ {
+ tagHelper.SrcInclude = "*.js";
+ tagHelper.SrcExclude = "*.min.js";
+ }
+ },
+ {
+ new Dictionary
+ {
+ ["asp-fallback-src"] = "test.js",
+ ["asp-fallback-test"] = "isavailable()"
+ },
+ tagHelper =>
+ {
+ tagHelper.FallbackSrc = "test.js";
+ tagHelper.FallbackTestExpression = "isavailable()";
+ }
+ },
+ {
+ new Dictionary
+ {
+ ["asp-fallback-src-include"] = "*.js",
+ ["asp-fallback-test"] = "isavailable()"
+ },
+ tagHelper =>
+ {
+ tagHelper.FallbackSrcInclude = "*.css";
+ tagHelper.FallbackTestExpression = "isavailable()";
+ }
+ },
+ {
+ new Dictionary
+ {
+ ["asp-fallback-src"] = "test.js",
+ ["asp-fallback-src-include"] = "*.js",
+ ["asp-fallback-test"] = "isavailable()"
+ },
+ tagHelper =>
+ {
+ tagHelper.FallbackSrc = "test.js";
+ tagHelper.FallbackSrcInclude = "*.css";
+ tagHelper.FallbackTestExpression = "isavailable()";
+ }
+ },
+ {
+ new Dictionary
+ {
+ ["asp-fallback-src-include"] = "*.js",
+ ["asp-fallback-src-exclude"] = "*.min.js",
+ ["asp-fallback-test"] = "isavailable()"
+ },
+ tagHelper =>
+ {
+ tagHelper.FallbackSrcInclude = "*.css";
+ tagHelper.FallbackSrcExclude = "*.min.css";
+ tagHelper.FallbackTestExpression = "isavailable()";
+ }
+ }
+ };
+ }
+ }
+
[Theory]
- [InlineData("~/blank.js")]
- [InlineData(null)]
- public async Task RunsWhenRequiredAttributesArePresent(string srcValue)
+ [MemberData(nameof(RunsWhenRequiredAttributesArePresent_Data))]
+ public async Task RunsWhenRequiredAttributesArePresent(
+ IDictionary attributes,
+ Action setProperties)
{
// Arrange
- var attributes = new Dictionary
- {
- ["asp-fallback-src"] = "http://www.example.com/blank.js",
- ["asp-fallback-test"] = "isavailable()",
- };
-
- if (srcValue != null)
- {
- attributes.Add("src", srcValue);
- }
-
- var tagHelperContext = MakeTagHelperContext(attributes);
- var viewContext = MakeViewContext();
-
+ var context = MakeTagHelperContext(attributes);
var output = MakeTagHelperOutput("script");
var logger = CreateLogger();
-
- var helper = new ScriptTagHelper()
+ var hostingEnvironment = MakeHostingEnvironment();
+ var viewContext = MakeViewContext();
+ var helper = new ScriptTagHelper
{
Logger = logger,
+ HostingEnvironment = hostingEnvironment,
ViewContext = viewContext,
- FallbackSrc = "http://www.example.com/blank.js",
- FallbackTestExpression = "isavailable()",
};
+ setProperties(helper);
// Act
- await helper.ProcessAsync(tagHelperContext, output);
+ await helper.ProcessAsync(context, output);
// Assert
Assert.Null(output.TagName);
@@ -61,57 +136,113 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Assert.Empty(logger.Logged);
}
- public static TheoryData MissingAttributeDataSet
+ public static TheoryData DoesNotRunWhenARequiredAttributeIsMissing_Data
{
get
{
- return new TheoryData, ScriptTagHelper, string>
+ return new TheoryData, Action>
{
{
- new Dictionary // the attributes provided
+ new Dictionary
{
- ["asp-fallback-src"] = "http://www.example.com/blank.js",
+ // This is commented out on purpose: ["asp-src-include"] = "*.js",
+ ["asp-src-exclude"] = "*.min.js"
},
- new ScriptTagHelper() // the tag helper
+ tagHelper =>
{
- FallbackTestExpression = "isavailable()",
- },
- "asp-fallback-test" // missing attribute
+ // This is commented out on purpose: tagHelper.SrcInclude = "*.js";
+ tagHelper.SrcExclude = "*.min.js";
+ }
},
-
{
- new Dictionary // the attributes provided
+ new Dictionary
{
+ // This is commented out on purpose: ["asp-fallback-src"] = "test.js",
["asp-fallback-test"] = "isavailable()",
},
- new ScriptTagHelper() // the tag helper
+ tagHelper =>
{
- FallbackTestExpression = "http://www.example.com/blank.js",
- },
- "asp-fallback-src" // missing attribute
+ // This is commented out on purpose: tagHelper.FallbackSrc = "test.js";
+ tagHelper.FallbackTestExpression = "isavailable()";
+ }
},
+ {
+ new Dictionary
+ {
+ ["asp-fallback-src"] = "test.js",
+ // This is commented out on purpose: ["asp-fallback-test"] = "isavailable()"
+ },
+ tagHelper =>
+ {
+ tagHelper.FallbackSrc = "test.js";
+ // This is commented out on purpose: tagHelper.FallbackTestExpression = "isavailable()";
+ }
+ },
+ {
+ new Dictionary
+ {
+ // This is commented out on purpose: ["asp-fallback-src-include"] = "test.js",
+ ["asp-fallback-src-exclude"] = "**/*.min.js",
+ ["asp-fallback-test"] = "isavailable()",
+ },
+ tagHelper =>
+ {
+ // This is commented out on purpose: tagHelper.FallbackSrcInclude = "test.js";
+ tagHelper.FallbackSrcExclude = "**/*.min.js";
+ tagHelper.FallbackTestExpression = "isavailable()";
+ }
+ }
};
}
}
[Theory]
- [MemberData(nameof(MissingAttributeDataSet))]
- public async Task DoesNotRunWhenARequiredAttributeIsMissing(
- Dictionary attributes,
- ScriptTagHelper helper,
- string attributeMissing)
+ [MemberData(nameof(DoesNotRunWhenARequiredAttributeIsMissing_Data))]
+ public void DoesNotRunWhenARequiredAttributeIsMissing(
+ IDictionary attributes,
+ Action setProperties)
{
// Arrange
- Assert.Single(attributes);
-
var tagHelperContext = MakeTagHelperContext(attributes);
+ var output = MakeTagHelperOutput("script");
+ var logger = new Mock>();
+ var hostingEnvironment = MakeHostingEnvironment();
var viewContext = MakeViewContext();
+ var helper = new ScriptTagHelper
+ {
+ Logger = logger.Object,
+ HostingEnvironment = hostingEnvironment,
+ ViewContext = viewContext
+ };
+ setProperties(helper);
+ // Act
+ helper.Process(tagHelperContext, output);
+
+ // Assert
+ Assert.NotNull(output.TagName);
+ Assert.False(output.ContentSet);
+ }
+
+ [Theory]
+ [MemberData(nameof(DoesNotRunWhenARequiredAttributeIsMissing_Data))]
+ public async Task LogsWhenARequiredAttributeIsMissing(
+ IDictionary attributes,
+ Action setProperties)
+ {
+ // Arrange
+ var tagHelperContext = MakeTagHelperContext(attributes);
var output = MakeTagHelperOutput("script");
var logger = CreateLogger();
-
- helper.Logger = logger;
- helper.ViewContext = viewContext;
+ var hostingEnvironment = MakeHostingEnvironment();
+ var viewContext = MakeViewContext();
+ var helper = new ScriptTagHelper
+ {
+ Logger = logger,
+ HostingEnvironment = hostingEnvironment,
+ ViewContext = viewContext
+ };
+ setProperties(helper);
// Act
await helper.ProcessAsync(tagHelperContext, output);
@@ -119,6 +250,18 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Assert
Assert.Equal("script", output.TagName);
Assert.False(output.ContentSet);
+
+ Assert.Equal(2, logger.Logged.Count);
+
+ Assert.Equal(LogLevel.Warning, logger.Logged[0].LogLevel);
+ Assert.IsAssignableFrom(logger.Logged[0].State);
+
+ var loggerData0 = (PartialModeMatchLoggerStructure)logger.Logged[0].State;
+
+ Assert.Equal(LogLevel.Verbose, logger.Logged[1].LogLevel);
+ Assert.IsAssignableFrom(logger.Logged[1].State);
+ Assert.StartsWith("Skipping processing for Microsoft.AspNet.Mvc.TagHelpers.ScriptTagHelper",
+ ((ILoggerStructure)logger.Logged[1].State).Format());
}
[Fact]
@@ -144,47 +287,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Assert.False(output.ContentSet);
}
- [Theory]
- [MemberData(nameof(MissingAttributeDataSet))]
- public async Task LogsWhenARequiredAttributeIsMissing(
- Dictionary attributes,
- ScriptTagHelper helper,
- string attributeMissing)
- {
- // Arrange
- Assert.Single(attributes);
-
- var tagHelperContext = MakeTagHelperContext(attributes);
- var viewContext = MakeViewContext();
-
- var output = MakeTagHelperOutput("script");
- var logger = CreateLogger();
-
- helper.Logger = logger;
- helper.ViewContext = viewContext;
-
- // Act
- await helper.ProcessAsync(tagHelperContext, output);
-
- // Assert
- Assert.Equal("script", output.TagName);
- Assert.False(output.ContentSet);
-
- Assert.Equal(2, logger.Logged.Count);
-
- Assert.Equal(LogLevel.Warning, logger.Logged[0].LogLevel);
- Assert.IsType(logger.Logged[0].State);
-
- var loggerData0 = (MissingAttributeLoggerStructure)logger.Logged[0].State;
- Assert.Single(loggerData0.MissingAttributes);
- Assert.Equal(attributeMissing, loggerData0.MissingAttributes.Single());
-
- Assert.Equal(LogLevel.Verbose, logger.Logged[1].LogLevel);
- Assert.IsAssignableFrom(logger.Logged[1].State);
- Assert.StartsWith("Skipping processing for ScriptTagHelper",
- ((ILoggerStructure)logger.Logged[1].State).Format());
- }
-
[Fact]
public async Task LogsWhenAllRequiredAttributesAreMissing()
{
@@ -211,7 +313,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Assert.Equal(LogLevel.Verbose, logger.Logged[0].LogLevel);
Assert.IsAssignableFrom(logger.Logged[0].State);
- Assert.StartsWith("Skipping processing for ScriptTagHelper",
+ Assert.StartsWith("Skipping processing for Microsoft.AspNet.Mvc.TagHelpers.ScriptTagHelper",
((ILoggerStructure)logger.Logged[0].State).Format());
}
@@ -231,7 +333,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var viewContext = MakeViewContext();
- var output = MakeTagHelperOutput("link",
+ var output = MakeTagHelperOutput("src",
attributes: new Dictionary
{
["data-extra"] = "something",
@@ -240,11 +342,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
});
var logger = CreateLogger();
+ var hostingEnvironment = MakeHostingEnvironment();
var helper = new ScriptTagHelper
{
Logger = logger,
ViewContext = viewContext,
+ HostingEnvironment = hostingEnvironment,
FallbackSrc = "~/blank.js",
FallbackTestExpression = "http://www.example.com/blank.js",
};
@@ -257,6 +361,42 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Assert.Empty(logger.Logged);
}
+ [Fact]
+ public async Task RendersScriptTagsForGlobbedSrcResults()
+ {
+ // Arrange
+ var context = MakeTagHelperContext(
+ attributes: new Dictionary
+ {
+ ["src"] = "/js/site.js",
+ ["asp-src-include"] = "**/*.js"
+ });
+ var output = MakeTagHelperOutput("script", attributes: new Dictionary
+ {
+ ["src"] = "/js/site.js"
+ });
+ var logger = new Mock>();
+ var hostingEnvironment = MakeHostingEnvironment();
+ var viewContext = MakeViewContext();
+ var globbingUrlBuilder = new Mock();
+ globbingUrlBuilder.Setup(g => g.BuildUrlList("/js/site.js", "**/*.js", null))
+ .Returns(new[] { "/js/site.js", "/common.js" });
+ var helper = new ScriptTagHelper
+ {
+ GlobbingUrlBuilder = globbingUrlBuilder.Object,
+ Logger = logger.Object,
+ HostingEnvironment = hostingEnvironment,
+ ViewContext = viewContext,
+ SrcInclude = "**/*.js"
+ };
+
+ // Act
+ await helper.ProcessAsync(context, output);
+
+ // Assert
+ Assert.Equal("", output.Content);
+ }
+
private TagHelperContext MakeTagHelperContext(
IDictionary attributes = null,
string content = null)
@@ -291,5 +431,19 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
return new TagHelperLogger();
}
+
+ private static IHostingEnvironment MakeHostingEnvironment()
+ {
+ var emptyDirectoryContents = new Mock();
+ emptyDirectoryContents.Setup(dc => dc.GetEnumerator())
+ .Returns(Enumerable.Empty().GetEnumerator());
+ var mockFileProvider = new Mock();
+ mockFileProvider.Setup(fp => fp.GetDirectoryContents(It.IsAny()))
+ .Returns(emptyDirectoryContents.Object);
+ var hostingEnvironment = new Mock();
+ hostingEnvironment.Setup(h => h.WebRootFileProvider).Returns(mockFileProvider.Object);
+
+ return hostingEnvironment.Object;
+ }
}
}
\ No newline at end of file
diff --git a/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Link.cshtml b/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Link.cshtml
index 8553cf89b9..f003ff1967 100644
--- a/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Link.cshtml
+++ b/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Link.cshtml
@@ -13,7 +13,7 @@
-
+
@@ -43,6 +43,35 @@
asp-fallback-test-property="visibility"
asp-fallback-test-value="hidden" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Script.cshtml b/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Script.cshtml
index dafcdfdc89..3f7c98c7cb 100644
--- a/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Script.cshtml
+++ b/test/WebSites/MvcTagHelpersWebSite/Views/MvcTagHelper_Home/Script.cshtml
@@ -11,20 +11,99 @@