Script Tag Helper
Implement Script TagHelper Add functional test Resolved #1576
This commit is contained in:
parent
12f8f23ccb
commit
e94cec51fa
|
|
@ -1,4 +1,7 @@
|
|||
|
||||
@*
|
||||
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 TagHelperSample.Web.Models
|
||||
@model IList<User>
|
||||
|
||||
|
|
@ -6,6 +9,20 @@
|
|||
Hello, you're in Development
|
||||
</environment>
|
||||
|
||||
<script src="~/originalXX.js" asp-fallback-src="~/fallback.js" asp-fallback-test="window.foo">
|
||||
// 1. By removing the src attribute, the script below will execute and prevent fallback from running
|
||||
// 2. Alternatively fix the type in the value of the src attribute
|
||||
function foo() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
function bar() {
|
||||
document.write("<p>foo is from page</p>");
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>bar();</script>
|
||||
|
||||
<h2>Index</h2>
|
||||
@if (Model != null && Model.Count() != 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
// 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.
|
||||
|
||||
function bar() {
|
||||
document.write("<p>foo is from fallback</p>");
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// 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.
|
||||
|
||||
function foo() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
function bar() {
|
||||
document.write("<p>foo is available</p>");
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
// 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;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
|
@ -9,15 +8,17 @@ using Microsoft.Framework.Logging;
|
|||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="ILoggerStructure"/> for log messages regarding <see cref="ITagHelper"/> instances that opt out of
|
||||
/// An <see cref="ILoggerStructure"/> for log messages regarding <see cref="TagHelper"/> instances that opt out of
|
||||
/// processing due to missing required attributes.
|
||||
/// </summary>
|
||||
public class MissingAttributeLoggerStructure : ILoggerStructure
|
||||
{
|
||||
private readonly string _uniqueId;
|
||||
private readonly IEnumerable<string> _missingAttributes;
|
||||
private readonly IEnumerable<KeyValuePair<string, object>> _values;
|
||||
|
||||
// internal for unit testing.
|
||||
internal IEnumerable<string> MissingAttributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="MissingAttributeLoggerStructure"/>.
|
||||
/// </summary>
|
||||
|
|
@ -26,11 +27,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
public MissingAttributeLoggerStructure(string uniqueId, IEnumerable<string> missingAttributes)
|
||||
{
|
||||
_uniqueId = uniqueId;
|
||||
_missingAttributes = missingAttributes;
|
||||
MissingAttributes = missingAttributes;
|
||||
_values = new Dictionary<string, object>
|
||||
{
|
||||
{ "UniqueId", _uniqueId },
|
||||
{ "MissingAttributes", _missingAttributes }
|
||||
{ "MissingAttributes", MissingAttributes }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +63,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
{
|
||||
return string.Format("Tag Helper unique ID: {0}, Missing attributes: {1}",
|
||||
_uniqueId,
|
||||
string.Join(",", _missingAttributes));
|
||||
string.Join(",", MissingAttributes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
// 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;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="ITagHelper"/> implementation targeting <script> elements that supports fallback src paths.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="FallbackSrc" /> and <see cref="FallbackTestExpression" /> are required to not be
|
||||
/// <c>null</c> or empty to process.
|
||||
/// </remarks>
|
||||
public class ScriptTagHelper : TagHelper
|
||||
{
|
||||
private const string FallbackSrcAttributeName = "asp-fallback-src";
|
||||
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,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The URL of a Script tag to fallback to in the case the primary one fails (as specified in the src
|
||||
/// attribute).
|
||||
/// </summary>
|
||||
[HtmlAttributeName(FallbackSrcAttributeName)]
|
||||
public string FallbackSrc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The script method defined in the primary script to use for the fallback test.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(FallbackTestExpressionAttributeName)]
|
||||
public string FallbackTestExpression { get; set; }
|
||||
|
||||
// Protected to ensure subclasses are correctly activated. Internal for ease of use when testing.
|
||||
[Activate]
|
||||
protected internal ILogger<ScriptTagHelper> Logger { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (!context.AllRequiredAttributesArePresent(RequiredAttributes, Logger))
|
||||
{
|
||||
if (Logger.IsEnabled(LogLevel.Verbose))
|
||||
{
|
||||
Logger.WriteVerbose("Skipping processing for {0} {1}", nameof(ScriptTagHelper), context.UniqueId);
|
||||
}
|
||||
|
||||
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 <script /> tag.
|
||||
content.Append("<script");
|
||||
foreach (var attribute in output.Attributes)
|
||||
{
|
||||
content.AppendFormat(CultureInfo.InvariantCulture, " {0}=\"{1}\"", attribute.Key, attribute.Value);
|
||||
}
|
||||
|
||||
content.Append(">");
|
||||
|
||||
var originalContent = await context.GetChildContentAsync();
|
||||
content.Append(originalContent)
|
||||
.AppendLine("</script>");
|
||||
|
||||
// Build the <script> tag that checks the test method and if it fails, renders the extra script.
|
||||
content.Append("<script>(")
|
||||
.Append(FallbackTestExpression)
|
||||
.Append("||document.write(\"<script");
|
||||
|
||||
if (!output.Attributes.ContainsKey("src"))
|
||||
{
|
||||
AppendSrc(content, "src");
|
||||
}
|
||||
|
||||
foreach (var attribute in output.Attributes)
|
||||
{
|
||||
if (!attribute.Key.Equals(SrcAttributeName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var encodedKey = JavaScriptUtility.JavaScriptStringEncode(attribute.Key);
|
||||
var encodedValue = JavaScriptUtility.JavaScriptStringEncode(attribute.Value);
|
||||
|
||||
content.AppendFormat(CultureInfo.InvariantCulture, " {0}=\\\"{1}\\\"", encodedKey, encodedValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendSrc(content, attribute.Key);
|
||||
}
|
||||
}
|
||||
|
||||
content.Append("><\\/script>\"));</script>");
|
||||
|
||||
output.Content = content.ToString();
|
||||
}
|
||||
|
||||
private void AppendSrc(StringBuilder content, string srcKey)
|
||||
{
|
||||
// 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("\\\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Script</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Script tag helper test</h2>
|
||||
<script src="/blank.js" data-foo="foo-data1">
|
||||
// Regular script with comment in body, and extra properties.
|
||||
</script>
|
||||
|
||||
<script src="/blank.js" data-foo="foo-data2">
|
||||
// TagHelper script with comment in body, and extra properties.
|
||||
</script>
|
||||
<script>(blank.wooptido||document.write("<script src=\"/fallback.js\" data-foo=\"foo-data2\"><\/script>"));</script>
|
||||
|
||||
<script data-foo="foo-data3">
|
||||
// Valid TagHelper (although no src is provided) script with comment in body, and extra properties.
|
||||
</script>
|
||||
<script>(blank.wooptido||document.write("<script src=\"/fallback.js\" data-foo=\"foo-data3\"><\/script>"));</script>
|
||||
|
||||
<script src="/blank.js">
|
||||
// Invalid TagHelper script with comment in body.
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -41,6 +41,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
[InlineData("Environment", null)]
|
||||
// Testing the LinkTagHelper
|
||||
[InlineData("Link", null)]
|
||||
// Testing the ScriptTagHelper
|
||||
[InlineData("Script", null)]
|
||||
public async Task MvcTagHelpers_GeneratesExpectedResults(string action, string antiForgeryPath)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -67,6 +69,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
var forgeryToken = AntiForgeryTestHelper.RetrieveAntiForgeryToken(responseContent, antiForgeryPath);
|
||||
expectedContent = string.Format(expectedContent, forgeryToken);
|
||||
}
|
||||
|
||||
Assert.Equal(expectedContent.Trim(), responseContent.Trim());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using Microsoft.Framework.Logging;
|
|||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers.Test
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
{
|
||||
public class LinkTagHelperTest
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,261 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
{
|
||||
public class ScriptTagHelperTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("~/blank.js")]
|
||||
[InlineData(null)]
|
||||
public async Task RunsWhenRequiredAttributesArePresent(string srcValue)
|
||||
{
|
||||
// Arrange
|
||||
var attributes = new Dictionary<string, object>
|
||||
{
|
||||
{ "asp-fallback-src", "http://www.example.com/blank.js" },
|
||||
{ "asp-fallback-test", "isavailable()" },
|
||||
};
|
||||
|
||||
if (srcValue != null)
|
||||
{
|
||||
attributes.Add("src", srcValue);
|
||||
}
|
||||
|
||||
var context = MakeTagHelperContext(attributes);
|
||||
|
||||
var output = MakeTagHelperOutput("script");
|
||||
var logger = CreateLogger();
|
||||
|
||||
var helper = new ScriptTagHelper()
|
||||
{
|
||||
Logger = logger,
|
||||
FallbackSrc = "http://www.example.com/blank.js",
|
||||
FallbackTestExpression = "isavailable()",
|
||||
};
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
Assert.Null(output.TagName);
|
||||
Assert.NotNull(output.Content);
|
||||
Assert.True(output.ContentSet);
|
||||
Assert.Empty(logger.Logged);
|
||||
}
|
||||
|
||||
public static TheoryData MissingAttributeDataSet
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<Dictionary<string, object>, ScriptTagHelper, string>
|
||||
{
|
||||
{
|
||||
new Dictionary<string, object> // the attributes provided
|
||||
{
|
||||
{ "asp-fallback-src", "http://www.example.com/blank.js" },
|
||||
},
|
||||
new ScriptTagHelper() // the tag helper
|
||||
{
|
||||
FallbackTestExpression = "isavailable()",
|
||||
},
|
||||
"asp-fallback-test" // missing attribute
|
||||
},
|
||||
|
||||
{
|
||||
new Dictionary<string, object> // the attributes provided
|
||||
{
|
||||
{ "asp-fallback-test", "isavailable()" },
|
||||
},
|
||||
new ScriptTagHelper() // the tag helper
|
||||
{
|
||||
FallbackTestExpression = "http://www.example.com/blank.js",
|
||||
},
|
||||
"asp-fallback-src" // missing attribute
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(MissingAttributeDataSet))]
|
||||
public async Task DoesNotRunWhenARequiredAttributeIsMissing(
|
||||
Dictionary<string, object> attributes,
|
||||
ScriptTagHelper helper,
|
||||
string attributeMissing)
|
||||
{
|
||||
// Arrange
|
||||
Assert.Single(attributes);
|
||||
|
||||
var context = MakeTagHelperContext(attributes);
|
||||
|
||||
var output = MakeTagHelperOutput("script");
|
||||
var logger = CreateLogger();
|
||||
|
||||
helper.Logger = logger;
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("script", output.TagName);
|
||||
Assert.False(output.ContentSet);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DoesNotRunWhenAllRequiredAttributesAreMissing()
|
||||
{
|
||||
// Arrange
|
||||
var context = MakeTagHelperContext();
|
||||
var output = MakeTagHelperOutput("script");
|
||||
var logger = CreateLogger();
|
||||
|
||||
var helper = new ScriptTagHelper
|
||||
{
|
||||
Logger = logger,
|
||||
};
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("script", output.TagName);
|
||||
Assert.False(output.ContentSet);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(MissingAttributeDataSet))]
|
||||
public async Task LogsWhenARequiredAttributeIsMissing(
|
||||
Dictionary<string, object> attributes,
|
||||
ScriptTagHelper helper,
|
||||
string attributeMissing)
|
||||
{
|
||||
// Arrange
|
||||
Assert.Single(attributes);
|
||||
|
||||
var context = MakeTagHelperContext(attributes);
|
||||
|
||||
var output = MakeTagHelperOutput("script");
|
||||
var logger = CreateLogger();
|
||||
|
||||
helper.Logger = logger;
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, 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<MissingAttributeLoggerStructure>(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<ILoggerStructure>(logger.Logged[1].State);
|
||||
Assert.StartsWith("Skipping processing for ScriptTagHelper",
|
||||
((ILoggerStructure)logger.Logged[1].State).Format());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LogsWhenAllRequiredAttributesAreMissing()
|
||||
{
|
||||
// Arrange
|
||||
var context = MakeTagHelperContext();
|
||||
var output = MakeTagHelperOutput("script");
|
||||
var logger = CreateLogger();
|
||||
|
||||
var helper = new ScriptTagHelper
|
||||
{
|
||||
Logger = logger,
|
||||
};
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("script", output.TagName);
|
||||
Assert.False(output.ContentSet);
|
||||
|
||||
Assert.Single(logger.Logged);
|
||||
|
||||
Assert.Equal(LogLevel.Verbose, logger.Logged[0].LogLevel);
|
||||
Assert.IsAssignableFrom<ILoggerStructure>(logger.Logged[0].State);
|
||||
Assert.StartsWith("Skipping processing for ScriptTagHelper",
|
||||
((ILoggerStructure)logger.Logged[0].State).Format());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreservesOrderOfSourceAttributesWhenRun()
|
||||
{
|
||||
// Arrange
|
||||
var context = MakeTagHelperContext(
|
||||
attributes: new Dictionary<string, object>
|
||||
{
|
||||
{ "data-extra", "something"},
|
||||
{ "src", "/blank.js"},
|
||||
{ "data-more", "else"},
|
||||
{ "asp-fallback-src", "http://www.example.com/blank.js" },
|
||||
{ "asp-fallback-test", "isavailable()" },
|
||||
});
|
||||
|
||||
var output = MakeTagHelperOutput("link",
|
||||
attributes: new Dictionary<string, string>
|
||||
{
|
||||
{ "data-extra", "something"},
|
||||
{ "src", "/blank.js"},
|
||||
{ "data-more", "else"},
|
||||
});
|
||||
|
||||
var logger = CreateLogger();
|
||||
|
||||
var helper = new ScriptTagHelper
|
||||
{
|
||||
Logger = logger,
|
||||
FallbackSrc = "~/blank.js",
|
||||
FallbackTestExpression = "http://www.example.com/blank.js",
|
||||
};
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
Assert.StartsWith("<script data-extra=\"something\" src=\"/blank.js\" data-more=\"else\"", output.Content);
|
||||
Assert.Empty(logger.Logged);
|
||||
}
|
||||
|
||||
private TagHelperContext MakeTagHelperContext(
|
||||
IDictionary<string, object> attributes = null,
|
||||
string content = null)
|
||||
{
|
||||
attributes = attributes ?? new Dictionary<string, object>();
|
||||
|
||||
return new TagHelperContext(attributes, Guid.NewGuid().ToString("N"), () => Task.FromResult(content));
|
||||
}
|
||||
|
||||
private TagHelperOutput MakeTagHelperOutput(string tagName, IDictionary<string, string> attributes = null)
|
||||
{
|
||||
attributes = attributes ?? new Dictionary<string, string>();
|
||||
|
||||
return new TagHelperOutput(tagName, attributes);
|
||||
}
|
||||
|
||||
private TagHelperLogger<ScriptTagHelper> CreateLogger()
|
||||
{
|
||||
return new TagHelperLogger<ScriptTagHelper>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
{
|
||||
public class TagHelperLogger<T> : ILogger<T>
|
||||
{
|
||||
public List<LoggerData> Logged { get; } = new List<LoggerData>();
|
||||
|
||||
public IDisposable BeginScope(object state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Write(LogLevel logLevel, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
|
||||
{
|
||||
Logged.Add(new LoggerData(logLevel, state));
|
||||
}
|
||||
|
||||
public class LoggerData
|
||||
{
|
||||
public LoggerData(LogLevel logLevel, object state)
|
||||
{
|
||||
LogLevel = logLevel;
|
||||
State = state;
|
||||
}
|
||||
|
||||
public LogLevel LogLevel { get; set; }
|
||||
public object State { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -160,5 +160,10 @@ namespace MvcTagHelpersWebSite.Controllers
|
|||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Script()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
@*
|
||||
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.
|
||||
*@
|
||||
@addtaghelper "*, Microsoft.AspNet.Mvc.TagHelpers"
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Script</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Script tag helper test</h2>
|
||||
<script src="~/blank.js" data-foo="foo-data1">
|
||||
// Regular script with comment in body, and extra properties.
|
||||
</script>
|
||||
|
||||
<script src="~/blank.js" asp-fallback-src="~/fallback.js" asp-fallback-test="blank.wooptido" data-foo="foo-data2">
|
||||
// TagHelper script with comment in body, and extra properties.
|
||||
</script>
|
||||
|
||||
<script asp-fallback-src="~/fallback.js" asp-fallback-test="blank.wooptido" data-foo="foo-data3">
|
||||
// Valid TagHelper (although no src is provided) script with comment in body, and extra properties.
|
||||
</script>
|
||||
|
||||
<script src="~/blank.js" asp-fallback-src="~/fallback.js">
|
||||
// Invalid TagHelper script with comment in body.
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue