Add `TagHelperOutput.GetChildContentAsync()` overloads including an `HtmlEncoder`

- #643 part 1
- change is viral and requires an update to `RazorPage.StartTagHelperWritingScope()`
  - memoize `GetChildContentAsync()` per-encoder
  - update generation tests to match and to test new behaviour
- note `HtmlEncoder`s used elsewhere e.g. in other `RazorPage` instances are unaffected

Add `NullHtmlEncoder`

Nits:
- generally clean up affected doc comments and make them more consistent
- remove unused `using`s in files I had open
This commit is contained in:
Doug Bunting 2016-01-17 22:34:19 -08:00
parent e68c55ab41
commit 37850b5d85
23 changed files with 1409 additions and 803 deletions

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.Razor.TagHelpers;
@ -15,9 +16,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
private readonly List<ITagHelper> _tagHelpers;
private readonly Func<Task> _executeChildContentAsync;
private readonly Action _startTagHelperWritingScope;
private readonly Action<HtmlEncoder> _startTagHelperWritingScope;
private readonly Func<TagHelperContent> _endTagHelperWritingScope;
private TagHelperContent _childContent;
private Dictionary<HtmlEncoder, TagHelperContent> _perEncoderChildContent;
/// <summary>
/// Internal for testing purposes only.
@ -28,7 +30,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: async () => await Task.FromResult(result: true),
startTagHelperWritingScope: () => { },
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent())
{
}
@ -42,7 +44,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
/// <see cref="ITagHelper"/>s</param>
/// <param name="uniqueId">An identifier unique to the HTML element this context is for.</param>
/// <param name="executeChildContentAsync">A delegate used to execute the child content asynchronously.</param>
/// <param name="startTagHelperWritingScope">A delegate used to start a writing scope in a Razor page.</param>
/// <param name="startTagHelperWritingScope">
/// A delegate used to start a writing scope in a Razor page and optionally override the page's
/// <see cref="HtmlEncoder"/> within that scope.
/// </param>
/// <param name="endTagHelperWritingScope">A delegate used to end a writing scope in a Razor page.</param>
public TagHelperExecutionContext(
string tagName,
@ -50,7 +55,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
IDictionary<object, object> items,
string uniqueId,
Func<Task> executeChildContentAsync,
Action startTagHelperWritingScope,
Action<HtmlEncoder> startTagHelperWritingScope,
Func<TagHelperContent> endTagHelperWritingScope)
{
if (tagName == null)
@ -224,23 +229,57 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}
/// <summary>
/// Execute and retrieve the rendered child content asynchronously.
/// Executes children asynchronously with the given <paramref name="encoder"/> in scope and returns their
/// rendered content.
/// </summary>
/// <param name="useCachedResult">
/// If <c>true</c>, multiple calls with the same <see cref="HtmlEncoder"/> will not cause children to
/// re-execute; returns cached content.
/// </param>
/// <param name="encoder">
/// The <see cref="HtmlEncoder"/> to use when the page handles
/// non-<see cref="Microsoft.AspNet.Html.IHtmlContent"/> C# expressions. If <c>null</c>, executes children with
/// the page's current <see cref="HtmlEncoder"/>.
/// </param>
/// <returns>A <see cref="Task"/> that on completion returns the rendered child content.</returns>
/// <remarks>
/// Child content is only executed once. Successive calls to this method or successive executions of the
/// returned <see cref="Task{TagHelperContent}"/> return a cached result.
/// </remarks>
public async Task<TagHelperContent> GetChildContentAsync(bool useCachedResult)
public async Task<TagHelperContent> GetChildContentAsync(bool useCachedResult, HtmlEncoder encoder)
{
if (!useCachedResult || _childContent == null)
// Get cached content for this encoder.
TagHelperContent childContent;
if (encoder == null)
{
_startTagHelperWritingScope();
await _executeChildContentAsync();
_childContent = _endTagHelperWritingScope();
childContent = _childContent;
}
else
{
if (_perEncoderChildContent == null)
{
childContent = null;
_perEncoderChildContent = new Dictionary<HtmlEncoder, TagHelperContent>();
}
else
{
_perEncoderChildContent.TryGetValue(encoder, out childContent);
}
}
return new DefaultTagHelperContent().SetContent(_childContent);
if (!useCachedResult || childContent == null)
{
_startTagHelperWritingScope(encoder);
await _executeChildContentAsync();
childContent = _endTagHelperWritingScope();
if (encoder == null)
{
_childContent = childContent;
}
else
{
_perEncoderChildContent[encoder] = childContent;
}
}
return new DefaultTagHelperContent().SetContent(childContent);
}
}
}

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Razor.TagHelpers;

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.Extensions.Internal;
@ -31,7 +32,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
/// <param name="tagMode">HTML syntax of the element in the Razor source.</param>
/// <param name="uniqueId">An identifier unique to the HTML element this scope is for.</param>
/// <param name="executeChildContentAsync">A delegate used to execute the child content asynchronously.</param>
/// <param name="startTagHelperWritingScope">A delegate used to start a writing scope in a Razor page.</param>
/// <param name="startTagHelperWritingScope">
/// A delegate used to start a writing scope in a Razor page and optionally override the page's
/// <see cref="HtmlEncoder"/> within that scope.
/// </param>
/// <param name="endTagHelperWritingScope">A delegate used to end a writing scope in a Razor page.</param>
/// <returns>A <see cref="TagHelperExecutionContext"/> to use.</returns>
public TagHelperExecutionContext Begin(
@ -39,7 +43,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
TagMode tagMode,
string uniqueId,
Func<Task> executeChildContentAsync,
Action startTagHelperWritingScope,
Action<HtmlEncoder> startTagHelperWritingScope,
Func<TagHelperContent> endTagHelperWritingScope)
{
if (tagName == null)

View File

@ -0,0 +1,120 @@
// Copyright (c) .NET Foundation. 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.IO;
using System.Text.Encodings.Web;
namespace Microsoft.AspNet.Razor.TagHelpers
{
/// <summary>
/// A <see cref="HtmlEncoder"/> that does not encode. Should not be used when writing directly to a response
/// expected to contain valid HTML.
/// </summary>
public class NullHtmlEncoder : HtmlEncoder
{
/// <summary>
/// Initializes a <see cref="NullHtmlEncoder"/> instance.
/// </summary>
protected NullHtmlEncoder()
{
}
/// <summary>
/// A <see cref="HtmlEncoder"/> instance that does not encode. Should not be used when writing directly to a
/// response expected to contain valid HTML.
/// </summary>
public static new NullHtmlEncoder Default { get; } = new NullHtmlEncoder();
/// <inheritdoc />
public override int MaxOutputCharactersPerInputCharacter
{
get
{
return 1;
}
}
/// <inheritdoc />
public override string Encode(string value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
return value;
}
/// <inheritdoc />
public override void Encode(TextWriter output, char[] value, int startIndex, int characterCount)
{
if (output == null)
{
throw new ArgumentNullException(nameof(output));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (characterCount == 0)
{
return;
}
output.Write(value, startIndex, characterCount);
}
/// <inheritdoc />
public override void Encode(TextWriter output, string value, int startIndex, int characterCount)
{
if (output == null)
{
throw new ArgumentNullException(nameof(output));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (characterCount == 0)
{
return;
}
output.Write(value.Substring(startIndex, characterCount));
}
/// <inheritdoc />
public override unsafe int FindFirstCharacterToEncode(char* text, int textLength)
{
return -1;
}
/// <inheritdoc />
public override unsafe bool TryEncodeUnicodeScalar(
int unicodeScalar,
char* buffer,
int bufferLength,
out int numberOfCharactersWritten)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
numberOfCharactersWritten = 0;
return false;
}
/// <inheritdoc />
public override bool WillEncode(int unicodeScalar)
{
return false;
}
}
}

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
/// </summary>
public class TagHelperOutput : IHtmlContent
{
private readonly Func<bool, Task<TagHelperContent>> _getChildContentAsync;
private readonly Func<bool, HtmlEncoder, Task<TagHelperContent>> _getChildContentAsync;
private TagHelperContent _preElement;
private TagHelperContent _preContent;
private TagHelperContent _content;
@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
: this(
tagName,
new TagHelperAttributeList(),
(cachedResult) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()))
(useCachedResult, encoder) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()))
{
}
@ -36,12 +36,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers
/// </summary>
/// <param name="tagName">The HTML element's tag name.</param>
/// <param name="attributes">The HTML attributes.</param>
/// <param name="getChildContentAsync">A delegate used to execute and retrieve the rendered child content
/// asynchronously.</param>
/// <param name="getChildContentAsync">
/// A delegate used to execute children asynchronously with the given <see cref="HtmlEncoder"/> in scope and
/// return their rendered content.
/// </param>
public TagHelperOutput(
string tagName,
TagHelperAttributeList attributes,
Func<bool, Task<TagHelperContent>> getChildContentAsync)
Func<bool, HtmlEncoder, Task<TagHelperContent>> getChildContentAsync)
{
if (attributes == null)
{
@ -206,24 +208,65 @@ namespace Microsoft.AspNet.Razor.TagHelpers
}
/// <summary>
/// A delegate used to execute children asynchronously.
/// Executes children asynchronously and returns their rendered content.
/// </summary>
/// <returns>A <see cref="Task"/> that on completion returns content rendered by children.</returns>
/// <remarks>This method is memoized.</remarks>
/// <remarks>
/// This method is memoized. Multiple calls will not cause children to re-execute with the page's original
/// <see cref="HtmlEncoder"/>.
/// </remarks>
public Task<TagHelperContent> GetChildContentAsync()
{
return GetChildContentAsync(useCachedResult: true);
return GetChildContentAsync(useCachedResult: true, encoder: null);
}
/// <summary>
/// A delegate used to execute children asynchronously.
/// Executes children asynchronously and returns their rendered content.
/// </summary>
/// <param name="useCachedResult">If <c>true</c> multiple calls to this method will not cause re-execution
/// of child content; cached content will be returned.</param>
/// <param name="useCachedResult">
/// If <c>true</c>, multiple calls will not cause children to re-execute with the page's original
/// <see cref="HtmlEncoder"/>; returns cached content.
/// </param>
/// <returns>A <see cref="Task"/> that on completion returns content rendered by children.</returns>
public Task<TagHelperContent> GetChildContentAsync(bool useCachedResult)
{
return _getChildContentAsync(useCachedResult);
return GetChildContentAsync(useCachedResult, encoder: null);
}
/// <summary>
/// Executes children asynchronously with the given <paramref name="encoder"/> in scope and returns their
/// rendered content.
/// </summary>
/// <param name="encoder">
/// The <see cref="HtmlEncoder"/> to use when the page handles non-<see cref="IHtmlContent"/> C# expressions.
/// If <c>null</c>, executes children with the page's current <see cref="HtmlEncoder"/>.
/// </param>
/// <returns>A <see cref="Task"/> that on completion returns content rendered by children.</returns>
/// <remarks>
/// This method is memoized. Multiple calls with the same <see cref="HtmlEncoder"/> instance will not cause
/// children to re-execute with that encoder in scope.
/// </remarks>
public Task<TagHelperContent> GetChildContentAsync(HtmlEncoder encoder)
{
return GetChildContentAsync(useCachedResult: true, encoder: encoder);
}
/// <summary>
/// Executes children asynchronously with the given <paramref name="encoder"/> in scope and returns their
/// rendered content.
/// </summary>
/// <param name="useCachedResult">
/// If <c>true</c>, multiple calls with the same <see cref="HtmlEncoder"/> will not cause children to
/// re-execute; returns cached content.
/// </param>
/// <param name="encoder">
/// The <see cref="HtmlEncoder"/> to use when the page handles non-<see cref="IHtmlContent"/> C# expressions.
/// If <c>null</c>, executes children with the page's current <see cref="HtmlEncoder"/>.
/// </param>
/// <returns>A <see cref="Task"/> that on completion returns content rendered by children.</returns>
public Task<TagHelperContent> GetChildContentAsync(bool useCachedResult, HtmlEncoder encoder)
{
return _getChildContentAsync(useCachedResult, encoder);
}
/// <inheritdoc />

View File

@ -2,8 +2,9 @@
"description": "Runtime components for rendering Razor pages.",
"version": "4.0.0-*",
"compilationOptions": {
"warningsAsErrors": true,
"keyFile": "../../tools/Key.snk"
"allowUnsafe": true,
"keyFile": "../../tools/Key.snk",
"warningsAsErrors": true
},
"repository": {
"type": "git",

View File

@ -8,8 +8,8 @@ using System.Globalization;
using System.Linq;
using Microsoft.AspNet.Razor.Chunks;
using Microsoft.AspNet.Razor.CodeGenerators.Visitors;
using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.AspNet.Razor.Compilation.TagHelpers;
using Microsoft.AspNet.Razor.TagHelpers;
namespace Microsoft.AspNet.Razor.CodeGenerators
{
@ -659,7 +659,7 @@ namespace Microsoft.AspNet.Razor.CodeGenerators
// Scopes are a runtime feature.
if (!_designTimeMode)
{
_writer.WriteMethodInvocation(_tagHelperContext.StartTagHelperWritingScopeMethodName);
_writer.WriteMethodInvocation(_tagHelperContext.StartTagHelperWritingScopeMethodName, "null");
}
var visitor = htmlEncodeValues ? _bodyVisitor : _literalBodyVisitor;
@ -668,8 +668,9 @@ namespace Microsoft.AspNet.Razor.CodeGenerators
// Scopes are a runtime feature.
if (!_designTimeMode)
{
_writer.WriteStartAssignment(StringValueBufferVariableName)
.WriteMethodInvocation(_tagHelperContext.EndTagHelperWritingScopeMethodName);
_writer
.WriteStartAssignment(StringValueBufferVariableName)
.WriteMethodInvocation(_tagHelperContext.EndTagHelperWritingScopeMethodName);
}
}
finally
@ -708,7 +709,7 @@ namespace Microsoft.AspNet.Razor.CodeGenerators
StringValueBufferVariableName,
_tagHelperContext.TagHelperContentGetContentMethodName,
endLine: false,
parameters: new string[] { _tagHelperContext.HtmlEncoderPropertyName });
parameters: _tagHelperContext.HtmlEncoderPropertyName);
}
}

View File

@ -1,9 +1,9 @@
// Copyright (c) .NET Foundation. 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.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.Extensions.WebEncoders.Testing;
@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
items: expectedItems,
uniqueId: string.Empty,
executeChildContentAsync: async () => await Task.FromResult(result: true),
startTagHelperWritingScope: () => { },
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Assert
@ -51,13 +51,47 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
Assert.Same(expectedItems, executionContext.Items);
}
[Fact]
public async Task GetChildContentAsync_CachesValue()
public static TheoryData<HtmlEncoder> HtmlEncoderData
{
get
{
return new TheoryData<HtmlEncoder>
{
null,
HtmlEncoder.Default,
NullHtmlEncoder.Default,
new HtmlTestEncoder(),
};
}
}
public static TheoryData<HtmlEncoder> HtmlEncoderDataLessNull
{
get
{
var data = new TheoryData<HtmlEncoder>();
foreach (var row in HtmlEncoderData)
{
var encoder = (HtmlEncoder)(row[0]);
if (encoder != null)
{
data.Add(encoder);
}
}
return data;
}
}
[Theory]
[MemberData(nameof(HtmlEncoderData))]
public async Task GetChildContentAsync_ReturnsExpectedContent(HtmlEncoder encoder)
{
// Arrange
var defaultTagHelperContent = new DefaultTagHelperContent();
var content = string.Empty;
var expectedContent = string.Empty;
var tagHelperContent = new DefaultTagHelperContent();
var executionCount = 0;
var content = "Hello from child content";
var expectedContent = $"HtmlEncode[[{content}]]";
var executionContext = new TagHelperExecutionContext(
"p",
tagMode: TagMode.StartTagAndEndTag,
@ -65,30 +99,46 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
uniqueId: string.Empty,
executeChildContentAsync: () =>
{
if (string.IsNullOrEmpty(expectedContent))
{
content = "Hello from child content: " + Guid.NewGuid().ToString();
expectedContent = $"HtmlEncode[[{content}]]";
}
defaultTagHelperContent.SetContent(content);
executionCount++;
tagHelperContent.SetContent(content);
return Task.FromResult(result: true);
},
startTagHelperWritingScope: () => { },
endTagHelperWritingScope: () => defaultTagHelperContent);
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => tagHelperContent);
// Act
var content1 = await executionContext.GetChildContentAsync(useCachedResult: true);
var content2 = await executionContext.GetChildContentAsync(useCachedResult: true);
var actualContent = await executionContext.GetChildContentAsync(useCachedResult: true, encoder: encoder);
// Assert
Assert.Equal(expectedContent, content1.GetContent(new HtmlTestEncoder()));
Assert.Equal(expectedContent, content2.GetContent(new HtmlTestEncoder()));
Assert.Equal(expectedContent, actualContent.GetContent(new HtmlTestEncoder()));
}
[Fact]
public async Task GetChildContentAsync_CanExecuteChildrenMoreThanOnce()
[Theory]
[MemberData(nameof(HtmlEncoderData))]
public async Task GetChildContentAsync_StartsWritingScopeWithGivenEncoder(HtmlEncoder encoder)
{
// Arrange
HtmlEncoder passedEncoder = null;
var executionContext = new TagHelperExecutionContext(
"p",
tagMode: TagMode.StartTagAndEndTag,
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: () => Task.FromResult(result: true),
startTagHelperWritingScope: encoderArgument => passedEncoder = encoderArgument,
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Act
await executionContext.GetChildContentAsync(useCachedResult: true, encoder: encoder);
// Assert
Assert.Same(encoder, passedEncoder);
}
[Theory]
[MemberData(nameof(HtmlEncoderData))]
public async Task GetChildContentAsync_CachesValue(HtmlEncoder encoder)
{
// Arrange
var executionCount = 0;
@ -100,15 +150,98 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
executeChildContentAsync: () =>
{
executionCount++;
return Task.FromResult(result: true);
},
startTagHelperWritingScope: () => { },
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Act
await executionContext.GetChildContentAsync(useCachedResult: false);
await executionContext.GetChildContentAsync(useCachedResult: false);
var content1 = await executionContext.GetChildContentAsync(useCachedResult: true, encoder: encoder);
var content2 = await executionContext.GetChildContentAsync(useCachedResult: true, encoder: encoder);
// Assert
Assert.Equal(1, executionCount);
}
[Theory]
[MemberData(nameof(HtmlEncoderDataLessNull))]
public async Task GetChildContentAsync_CachesValuePerEncoder(HtmlEncoder encoder)
{
// Arrange
var executionCount = 0;
var executionContext = new TagHelperExecutionContext(
"p",
tagMode: TagMode.StartTagAndEndTag,
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: () =>
{
executionCount++;
return Task.FromResult(result: true);
},
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Act
var content1 = await executionContext.GetChildContentAsync(useCachedResult: true, encoder: null);
var content2 = await executionContext.GetChildContentAsync(useCachedResult: true, encoder: encoder);
// Assert
Assert.Equal(2, executionCount);
}
[Theory]
[MemberData(nameof(HtmlEncoderData))]
public async Task GetChildContentAsync_CachesValuePerEncoderInstance(HtmlEncoder encoder)
{
// Arrange
var executionCount = 0;
var executionContext = new TagHelperExecutionContext(
"p",
tagMode: TagMode.StartTagAndEndTag,
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: () =>
{
executionCount++;
return Task.FromResult(result: true);
},
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// HtmlEncoderData includes another HtmlTestEncoder instance but method compares HtmlEncoder instances.
var firstEncoder = new HtmlTestEncoder();
// Act
var content1 = await executionContext.GetChildContentAsync(useCachedResult: true, encoder: firstEncoder);
var content2 = await executionContext.GetChildContentAsync(useCachedResult: true, encoder: encoder);
// Assert
Assert.Equal(2, executionCount);
}
[Theory]
[MemberData(nameof(HtmlEncoderData))]
public async Task GetChildContentAsync_CanExecuteChildrenMoreThanOnce(HtmlEncoder encoder)
{
// Arrange
var executionCount = 0;
var executionContext = new TagHelperExecutionContext(
"p",
tagMode: TagMode.StartTagAndEndTag,
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: () =>
{
executionCount++;
return Task.FromResult(result: true);
},
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Act
await executionContext.GetChildContentAsync(useCachedResult: false, encoder: encoder);
await executionContext.GetChildContentAsync(useCachedResult: false, encoder: encoder);
// Assert
Assert.Equal(2, executionCount);
@ -120,27 +253,21 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
public async Task GetChildContentAsync_ReturnsNewObjectEveryTimeItIsCalled(bool useCachedResult)
{
// Arrange
var defaultTagHelperContent = new DefaultTagHelperContent();
var executionContext = new TagHelperExecutionContext(
"p",
tagMode: TagMode.StartTagAndEndTag,
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: () => { return Task.FromResult(result: true); },
startTagHelperWritingScope: () => { },
endTagHelperWritingScope: () => defaultTagHelperContent);
executeChildContentAsync: () => Task.FromResult(result: true),
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Act
var content1 = await executionContext.GetChildContentAsync(useCachedResult);
content1.Append("Hello");
var content2 = await executionContext.GetChildContentAsync(useCachedResult);
content2.Append("World!");
var content1 = await executionContext.GetChildContentAsync(useCachedResult, encoder: null);
var content2 = await executionContext.GetChildContentAsync(useCachedResult, encoder: null);
// Assert
Assert.NotSame(content1, content2);
var content3 = await executionContext.GetChildContentAsync(useCachedResult);
Assert.Empty(content3.GetContent(new HtmlTestEncoder()));
}
public static TheoryData<string, string> DictionaryCaseTestingData
@ -314,7 +441,6 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
Assert.Equal(2, tagHelpers.Length);
Assert.Same(tagHelper1, tagHelpers[0]);
Assert.Same(tagHelper2, tagHelpers[1]);
}
private class PTagHelper : TagHelper

View File

@ -1,698 +0,0 @@
// Copyright (c) .NET Foundation. 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.IO;
using System.Threading.Tasks;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.Extensions.WebEncoders.Testing;
using Xunit;
namespace Microsoft.AspNet.Razor.Runtime.Test.Runtime.TagHelpers
{
public class TagHelperOutputTest
{
public static TheoryData<TagHelperOutput, string> WriteTagHelper_InputData
{
get
{
// parameters: TagHelperOutput, expectedOutput
return new TheoryData<TagHelperOutput, string>
{
{
// parameters: TagName, Attributes, SelfClosing, PreContent, Content, PostContent
GetTagHelperOutput(
tagName: "div",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<div>Hello World!</div>"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"Hello World!"
},
{
GetTagHelperOutput(
tagName: " ",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"Hello World!"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList() { { "test", "testVal" } },
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\">Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
{ "test", "testVal" },
{ "something", " spaced " }
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\" something=\"HtmlEncode[[ spaced ]]\">Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
["test2"] = new TagHelperAttribute
{
Name = "test2",
Minimized = true
},
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test test2>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
["first"] = "unminimized",
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p first=\"HtmlEncode[[unminimized]]\" test>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
["last"] = "unminimized",
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test last=\"HtmlEncode[[unminimized]]\">Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList() { { "test", "testVal" } },
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\" />"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
{ "test", "testVal" },
{ "something", " spaced " }
},
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\" something=\"HtmlEncode[[ spaced ]]\" />"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList() { { "test", "testVal" } },
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\">"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
{ "test", "testVal" },
{ "something", " spaced " }
},
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\" something=\"HtmlEncode[[ spaced ]]\">"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: "Hello World!",
content: null,
postContent: null,
postElement: null),
"<p>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: null,
postContent: "Hello World!",
postElement: null),
"<p>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<p>HelloTestWorld!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<p />"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<p>"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<custom>HelloTestWorld!</custom>"
},
{
GetTagHelperOutput(
tagName: "random",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<random />"
},
{
GetTagHelperOutput(
tagName: "random",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<random>"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom></custom>"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.SelfClosing,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.SelfClosing,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom test=\"HtmlEncode[[testVal]]\" />"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom />"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagOnly,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagOnly,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom test=\"HtmlEncode[[testVal]]\">"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom>"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom></custom>After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom test=\"HtmlEncode[[testVal]]\" />After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom />After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom test=\"HtmlEncode[[testVal]]\">After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom>After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"Before<custom>HelloTestWorld!</custom>After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"Before<custom test=\"HtmlEncode[[testVal]]\">HelloTestWorld!</custom>After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"Before<custom />After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"BeforeHelloTestWorld!After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"Before<custom>After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"BeforeHelloTestWorld!After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"BeforeHelloTestWorld!After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"BeforeHelloTestWorld!After"
},
};
}
}
[Theory]
[MemberData(nameof(WriteTagHelper_InputData))]
public void TagHelperOutput_WritesFormattedTagHelper(TagHelperOutput output, string expected)
{
// Arrange
var writer = new StringWriter();
var tagHelperExecutionContext = new TagHelperExecutionContext(
tagName: output.TagName,
tagMode: output.TagMode,
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: () => Task.FromResult(result: true),
startTagHelperWritingScope: () => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
tagHelperExecutionContext.Output = output;
var testEncoder = new HtmlTestEncoder();
// Act
output.WriteTo(writer, testEncoder);
// Assert
Assert.Equal(expected, writer.ToString(), StringComparer.Ordinal);
}
private static TagHelperOutput GetTagHelperOutput(
string tagName,
TagHelperAttributeList attributes,
TagMode tagMode,
string preElement,
string preContent,
string content,
string postContent,
string postElement)
{
var output = new TagHelperOutput(
tagName,
attributes,
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()))
{
TagMode = tagMode
};
if (preElement != null)
{
output.PreElement.AppendHtml(preElement);
}
if (preContent != null)
{
output.PreContent.AppendHtml(preContent);
}
if (content != null)
{
output.Content.AppendHtml(content);
}
if (postContent != null)
{
output.PostContent.AppendHtml(postContent);
}
if (postElement != null)
{
output.PostElement.AppendHtml(postElement);
}
return output;
}
}
}

View File

@ -167,7 +167,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
Assert.Equal(tagMode, executionContext.TagMode);
}
[Fact]
[Fact]
public void End_ReturnsParentExecutionContext()
{
// Arrange
@ -228,7 +228,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
tagMode,
uniqueId: string.Empty,
executeChildContentAsync: async () => await Task.FromResult(result: true),
startTagHelperWritingScope: () => { },
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
}
}

View File

@ -0,0 +1,158 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using Xunit;
namespace Microsoft.AspNet.Razor.TagHelpers
{
public class NullHtmlEncoderTest
{
[Fact]
public void MaxOutputCharactersPerInputCharacter_Returns1()
{
// Arrange
var encoder = NullHtmlEncoder.Default;
// Act
var result = encoder.MaxOutputCharactersPerInputCharacter;
// Assert
Assert.Equal(1, result);
}
[Theory]
[InlineData("")]
[InlineData("abcd")]
[InlineData("<<''\"\">>")]
public void Encode_String_DoesNotEncode(string value)
{
// Arrange
var encoder = NullHtmlEncoder.Default;
// Act
var result = encoder.Encode(value);
// Assert
Assert.Equal(value, result);
}
[Theory]
[InlineData("")]
[InlineData("abcd")]
[InlineData("<<''\"\">>")]
public void Encode_StringToTextWriter_DoesNotEncode(string value)
{
// Arrange
var encoder = NullHtmlEncoder.Default;
// Act
string result;
using (var writer = new StringWriter())
{
encoder.Encode(writer, value);
result = writer.ToString();
}
// Assert
Assert.Equal(value, result);
}
[Theory]
[InlineData("", 0, 0, "")]
[InlineData("abcd", 0, 0, "")]
[InlineData("<<''\"\">>", 0, 0, "")]
[InlineData("abcd", 0, 1, "a")]
[InlineData("<<''\"\">>", 0, 1, "<")]
[InlineData("abcd", 0, 4, "abcd")]
[InlineData("<<''\"\">>", 0, 8, "<<''\"\">>")]
[InlineData("abcd", 2, 0, "")]
[InlineData("<<''\"\">>", 2, 0, "")]
[InlineData("abcd", 2, 2, "cd")]
[InlineData("<<''\"\">>", 2, 2, "''")]
[InlineData("abcd", 3, 0, "")]
[InlineData("<<''\"\">>", 7, 0, "")]
[InlineData("abcd", 3, 1, "d")]
[InlineData("<<''\"\">>", 7, 1, ">")]
public void Encode_StringToTextWriter_DoesNotEncodeSubstring(
string value,
int startIndex,
int characterCount,
string expectedResult)
{
// Arrange
var encoder = NullHtmlEncoder.Default;
// Act
string result;
using (var writer = new StringWriter())
{
encoder.Encode(writer, value, startIndex, characterCount);
result = writer.ToString();
}
// Assert
Assert.Equal(expectedResult, result);
}
[Theory]
[InlineData("", 0, 0, "")]
[InlineData("abcd", 0, 0, "")]
[InlineData("<<''\"\">>", 0, 0, "")]
[InlineData("abcd", 0, 1, "a")]
[InlineData("<<''\"\">>", 0, 1, "<")]
[InlineData("abcd", 0, 4, "abcd")]
[InlineData("<<''\"\">>", 0, 8, "<<''\"\">>")]
[InlineData("abcd", 2, 0, "")]
[InlineData("<<''\"\">>", 2, 0, "")]
[InlineData("abcd", 2, 2, "cd")]
[InlineData("<<''\"\">>", 2, 2, "''")]
[InlineData("abcd", 3, 0, "")]
[InlineData("<<''\"\">>", 7, 0, "")]
[InlineData("abcd", 3, 1, "d")]
[InlineData("<<''\"\">>", 7, 1, ">")]
public void Encode_CharsToTextWriter_DoesNotEncodeSubstring(
string value,
int startIndex,
int characterCount,
string expectedResult)
{
// Arrange
var encoder = NullHtmlEncoder.Default;
var chars = value.ToCharArray();
// Act
string result;
using (var writer = new StringWriter())
{
encoder.Encode(writer, chars, startIndex, characterCount);
result = writer.ToString();
}
// Assert
Assert.Equal(expectedResult, result);
}
[Theory]
[InlineData(int.MaxValue)]
[InlineData(int.MinValue)]
[InlineData(0)]
[InlineData((int)'\n')]
[InlineData((int)'\r')]
[InlineData((int)'<')]
[InlineData((int)'>')]
[InlineData((int)'\'')]
[InlineData((int)'"')]
public void WillEncode_ReturnsFalse(int unicodeScalar)
{
// Arrange
var encoder = NullHtmlEncoder.Default;
// Act
var result = encoder.WillEncode(unicodeScalar);
// Assert
Assert.False(result);
}
}
}

View File

@ -1,7 +1,12 @@
// Copyright (c) .NET Foundation. 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.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Extensions.WebEncoders.Testing;
using Xunit;
@ -9,27 +14,148 @@ namespace Microsoft.AspNet.Razor.TagHelpers
{
public class TagHelperOutputTest
{
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task GetChildContentAsync_PassesUseCachedResultAsExpected(bool expectedUseCachedResultValue)
[Fact]
public async Task GetChildContentAsync_CallsGetChildContentAsync()
{
// Arrange
bool? useCachedResultValue = null;
bool? passedUseCacheResult = null;
HtmlEncoder passedEncoder = null;
var content = new DefaultTagHelperContent();
var output = new TagHelperOutput(
tagName: "p",
tagName: "tag",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
useCachedResultValue = useCachedResult;
return Task.FromResult<TagHelperContent>(new DefaultTagHelperContent());
passedUseCacheResult = useCachedResult;
passedEncoder = encoder;
return Task.FromResult<TagHelperContent>(content);
});
// Act
await output.GetChildContentAsync(expectedUseCachedResultValue);
var result = await output.GetChildContentAsync();
// Assert
Assert.Equal(expectedUseCachedResultValue, useCachedResultValue);
Assert.True(passedUseCacheResult.HasValue);
Assert.True(passedUseCacheResult.Value);
Assert.Null(passedEncoder);
Assert.Same(content, result);
}
public static TheoryData<HtmlEncoder> HtmlEncoderData
{
get
{
return new TheoryData<HtmlEncoder>
{
null,
HtmlEncoder.Default,
NullHtmlEncoder.Default,
new HtmlTestEncoder(),
};
}
}
[Theory]
[MemberData(nameof(HtmlEncoderData))]
public async Task GetChildContentAsync_CallsGetChildContentAsync(HtmlEncoder encoder)
{
// Arrange
bool? passedUseCacheResult = null;
HtmlEncoder passedEncoder = null;
var content = new DefaultTagHelperContent();
var output = new TagHelperOutput(
tagName: "tag",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoderArgument) =>
{
passedUseCacheResult = useCachedResult;
passedEncoder = encoderArgument;
return Task.FromResult<TagHelperContent>(content);
});
// Act
var result = await output.GetChildContentAsync(encoder);
// Assert
Assert.True(passedUseCacheResult.HasValue);
Assert.True(passedUseCacheResult.Value);
Assert.Same(encoder, passedEncoder);
Assert.Same(content, result);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task GetChildContentAsync_CallsGetChildContentAsync(bool useCachedResult)
{
// Arrange
bool? passedUseCacheResult = null;
HtmlEncoder passedEncoder = null;
var content = new DefaultTagHelperContent();
var output = new TagHelperOutput(
tagName: "tag",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResultArgument, encoder) =>
{
passedUseCacheResult = useCachedResultArgument;
passedEncoder = encoder;
return Task.FromResult<TagHelperContent>(content);
});
// Act
var result = await output.GetChildContentAsync(useCachedResult);
// Assert
Assert.True(passedUseCacheResult.HasValue);
Assert.Equal(useCachedResult, passedUseCacheResult.Value);
Assert.Null(passedEncoder);
Assert.Same(content, result);
}
public static TheoryData<bool, HtmlEncoder> UseCachedResultAndHtmlEncoderData
{
get
{
var data = new TheoryData<bool, HtmlEncoder>();
foreach (var useCachedResult in new[] { false, true })
{
foreach (var encoderEntry in HtmlEncoderData)
{
var encoder = (HtmlEncoder)(encoderEntry[0]);
data.Add(useCachedResult, encoder);
}
}
return data;
}
}
[Theory]
[MemberData(nameof(UseCachedResultAndHtmlEncoderData))]
public async Task GetChildContentAsync_CallsGetChildContentAsync(bool useCachedResult, HtmlEncoder encoder)
{
// Arrange
bool? passedUseCacheResult = null;
HtmlEncoder passedEncoder = null;
var content = new DefaultTagHelperContent();
var output = new TagHelperOutput(
tagName: "tag",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResultArgument, encoderArgument) =>
{
passedUseCacheResult = useCachedResultArgument;
passedEncoder = encoderArgument;
return Task.FromResult<TagHelperContent>(content);
});
// Act
var result = await output.GetChildContentAsync(useCachedResult, encoder);
// Assert
Assert.True(passedUseCacheResult.HasValue);
Assert.Equal(useCachedResult, passedUseCacheResult.Value);
Assert.Same(encoder, passedEncoder);
Assert.Same(content, result);
}
[Fact]
@ -175,13 +301,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers
public void SuppressOutput_PreventsTagOutput()
{
// Arrange
var tagHelperOutput = new TagHelperOutput("p",
var tagHelperOutput = new TagHelperOutput(
"p",
new TagHelperAttributeList
{
{ "class", "btn" },
{ "something", " spaced " }
},
(cachedResult) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
(cachedResult, encoder) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
tagHelperOutput.PreContent.Append("Pre Content");
tagHelperOutput.Content.Append("Content");
tagHelperOutput.PostContent.Append("Post Content");
@ -209,12 +336,13 @@ namespace Microsoft.AspNet.Razor.TagHelpers
public void Attributes_IgnoresCase(string originalName, string updateName)
{
// Arrange
var tagHelperOutput = new TagHelperOutput("p",
var tagHelperOutput = new TagHelperOutput(
"p",
new TagHelperAttributeList
{
{ originalName, "btn" },
},
(cachedResult) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
(cachedResult, encoder) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
// Act
tagHelperOutput.Attributes[updateName] = "super button";
@ -226,5 +354,687 @@ namespace Microsoft.AspNet.Razor.TagHelpers
attribute,
CaseSensitiveTagHelperAttributeComparer.Default);
}
public static TheoryData<TagHelperOutput, string> WriteTagHelper_InputData
{
get
{
// parameters: TagHelperOutput, expectedOutput
return new TheoryData<TagHelperOutput, string>
{
{
// parameters: TagName, Attributes, SelfClosing, PreContent, Content, PostContent
GetTagHelperOutput(
tagName: "div",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<div>Hello World!</div>"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"Hello World!"
},
{
GetTagHelperOutput(
tagName: " ",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"Hello World!"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList() { { "test", "testVal" } },
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\">Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
{ "test", "testVal" },
{ "something", " spaced " }
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\" something=\"HtmlEncode[[ spaced ]]\">Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
["test2"] = new TagHelperAttribute
{
Name = "test2",
Minimized = true
},
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test test2>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
["first"] = "unminimized",
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p first=\"HtmlEncode[[unminimized]]\" test>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
["last"] = "unminimized",
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test last=\"HtmlEncode[[unminimized]]\">Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList() { { "test", "testVal" } },
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\" />"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
{ "test", "testVal" },
{ "something", " spaced " }
},
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\" something=\"HtmlEncode[[ spaced ]]\" />"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList() { { "test", "testVal" } },
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\">"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList()
{
{ "test", "testVal" },
{ "something", " spaced " }
},
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p test=\"HtmlEncode[[testVal]]\" something=\"HtmlEncode[[ spaced ]]\">"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: "Hello World!",
content: null,
postContent: null,
postElement: null),
"<p>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: "Hello World!",
postContent: null,
postElement: null),
"<p>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: null,
postContent: "Hello World!",
postElement: null),
"<p>Hello World!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<p>HelloTestWorld!</p>"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<p />"
},
{
GetTagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<p>"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<custom>HelloTestWorld!</custom>"
},
{
GetTagHelperOutput(
tagName: "random",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<random />"
},
{
GetTagHelperOutput(
tagName: "random",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: null),
"<random>"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom></custom>"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.SelfClosing,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.SelfClosing,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom test=\"HtmlEncode[[testVal]]\" />"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom />"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagOnly,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagOnly,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom test=\"HtmlEncode[[testVal]]\">"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: "Before",
preContent: null,
content: null,
postContent: null,
postElement: null),
"Before<custom>"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom></custom>After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom test=\"HtmlEncode[[testVal]]\" />After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom />After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom test=\"HtmlEncode[[testVal]]\">After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: null,
preContent: null,
content: null,
postContent: null,
postElement: "After"),
"<custom>After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"Before<custom>HelloTestWorld!</custom>After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"Before<custom test=\"HtmlEncode[[testVal]]\">HelloTestWorld!</custom>After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"Before<custom />After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.SelfClosing,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"BeforeHelloTestWorld!After"
},
{
GetTagHelperOutput(
tagName: "custom",
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"Before<custom>After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagOnly,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"BeforeHelloTestWorld!After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList(),
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"BeforeHelloTestWorld!After"
},
{
GetTagHelperOutput(
tagName: string.Empty,
attributes: new TagHelperAttributeList { { "test", "testVal" } },
tagMode: TagMode.StartTagAndEndTag,
preElement: "Before",
preContent: "Hello",
content: "Test",
postContent: "World!",
postElement: "After"),
"BeforeHelloTestWorld!After"
},
};
}
}
[Theory]
[MemberData(nameof(WriteTagHelper_InputData))]
public void WriteTo_WritesFormattedTagHelper(TagHelperOutput output, string expected)
{
// Arrange
var writer = new StringWriter();
var tagHelperExecutionContext = new TagHelperExecutionContext(
tagName: output.TagName,
tagMode: output.TagMode,
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: () => Task.FromResult(result: true),
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
tagHelperExecutionContext.Output = output;
var testEncoder = new HtmlTestEncoder();
// Act
output.WriteTo(writer, testEncoder);
// Assert
Assert.Equal(expected, writer.ToString(), StringComparer.Ordinal);
}
private static TagHelperOutput GetTagHelperOutput(
string tagName,
TagHelperAttributeList attributes,
TagMode tagMode,
string preElement,
string preContent,
string content,
string postContent,
string postElement)
{
var output = new TagHelperOutput(
tagName,
attributes,
getChildContentAsync: (useCachedContent, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()))
{
TagMode = tagMode
};
if (preElement != null)
{
output.PreElement.AppendHtml(preElement);
}
if (preContent != null)
{
output.PreContent.AppendHtml(preContent);
}
if (content != null)
{
output.Content.AppendHtml(content);
}
if (postContent != null)
{
output.PostContent.AppendHtml(postContent);
}
if (postElement != null)
{
output.PostElement.AppendHtml(postElement);
}
return output;
}
}
}

View File

@ -16,5 +16,8 @@
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<DnxInvisibleFolder Include=".testPublish\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -51,7 +51,7 @@ namespace TestOutput
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper);
__TestNamespace_InputTagHelper2 = CreateTagHelper<global::TestNamespace.InputTagHelper2>();
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2);
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
WriteLiteral("2000 + ");
#line 6 "BasicTagHelpers.cshtml"
Write(ViewBag.DefaultInterval);

View File

@ -52,7 +52,7 @@ namespace TestOutput
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper);
__TestNamespace_InputTagHelper2 = CreateTagHelper<global::TestNamespace.InputTagHelper2>();
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2);
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
WriteLiteral("2000 + ");
#line 6 "BasicTagHelpers.cshtml"
Write(ViewBag.DefaultInterval);

View File

@ -119,7 +119,7 @@ namespace TestOutput
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper);
__TestNamespace_InputTagHelper2 = CreateTagHelper<global::TestNamespace.InputTagHelper2>();
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2);
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
#line 16 "ComplexTagHelpers.cshtml"
WriteLiteral(checkbox);
@ -163,7 +163,7 @@ namespace TestOutput
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper);
__TestNamespace_InputTagHelper2 = CreateTagHelper<global::TestNamespace.InputTagHelper2>();
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2);
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
#line 17 "ComplexTagHelpers.cshtml"
WriteLiteral(true ? "checkbox" : "anything");
@ -188,7 +188,7 @@ namespace TestOutput
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper);
__TestNamespace_InputTagHelper2 = CreateTagHelper<global::TestNamespace.InputTagHelper2>();
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2);
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
#line 18 "ComplexTagHelpers.cshtml"
if(true) {

View File

@ -103,7 +103,7 @@ WriteTo(__razor_attribute_value_writer, string.Empty);
, StartTagHelperWritingScope, EndTagHelperWritingScope);
__TestNamespace_InputTagHelper = CreateTagHelper<global::TestNamespace.InputTagHelper>();
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper);
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
WriteLiteral("prefix ");
#line 7 "DynamicAttributeTagHelpers.cshtml"
WriteLiteral(DateTime.Now);
@ -136,7 +136,7 @@ AddHtmlAttributeValue(" ", 210, DateTime.Now, 211, 14, false);
, StartTagHelperWritingScope, EndTagHelperWritingScope);
__TestNamespace_InputTagHelper = CreateTagHelper<global::TestNamespace.InputTagHelper>();
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper);
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
#line 9 "DynamicAttributeTagHelpers.cshtml"
WriteLiteral(long.MinValue);

View File

@ -44,7 +44,7 @@ namespace TestOutput
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper);
__TestNamespace_InputTagHelper2 = CreateTagHelper<global::TestNamespace.InputTagHelper2>();
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2);
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
#line 6 "EscapedTagHelpers.cshtml"
WriteLiteral(DateTime.Now);

View File

@ -54,7 +54,7 @@ namespace TestOutput
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper);
__TestNamespace_InputTagHelper2 = CreateTagHelper<global::TestNamespace.InputTagHelper2>();
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2);
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
WriteLiteral("2000 + ");
#line 8 "NestedScriptTagTagHelpers.cshtml"
Write(ViewBag.DefaultInterval);

View File

@ -172,7 +172,7 @@ __TestNamespace_InputTagHelper2.IntDictionaryProperty["salt"] = 37;
__TestNamespace_InputTagHelper2.StringDictionaryProperty["paprika"] = "another string";
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-paprika", __TestNamespace_InputTagHelper2.StringDictionaryProperty["paprika"]);
__TestNamespace_InputTagHelper1.StringDictionaryProperty["paprika"] = __TestNamespace_InputTagHelper2.StringDictionaryProperty["paprika"];
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
WriteLiteral("literate ");
#line 21 "PrefixedAttributeTagHelpers.cshtml"
WriteLiteral(literate);

View File

@ -172,7 +172,7 @@ __TestNamespace_InputTagHelper1.IntDictionaryProperty["salt"] = 37;
__TestNamespace_InputTagHelper1.StringDictionaryProperty["paprika"] = "another string";
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-paprika", __TestNamespace_InputTagHelper1.StringDictionaryProperty["paprika"]);
__TestNamespace_InputTagHelper2.StringDictionaryProperty["paprika"] = __TestNamespace_InputTagHelper1.StringDictionaryProperty["paprika"];
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
WriteLiteral("literate ");
#line 21 "PrefixedAttributeTagHelpers.cshtml"
WriteLiteral(literate);

View File

@ -76,7 +76,7 @@ namespace TestOutput
, StartTagHelperWritingScope, EndTagHelperWritingScope);
__TestNamespace_MyTagHelper = CreateTagHelper<global::TestNamespace.MyTagHelper>();
__tagHelperExecutionContext.Add(__TestNamespace_MyTagHelper);
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
WriteLiteral("Current Time: ");
#line 9 "TagHelpersInSection.cshtml"
WriteLiteral(DateTime.Now);

View File

@ -43,7 +43,7 @@ __TestNamespace_PTagHelper.Age = 1337;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("age", __TestNamespace_PTagHelper.Age);
StartTagHelperWritingScope();
StartTagHelperWritingScope(null);
#line 7 "TagHelpersWithWeirdlySpacedAttributes.cshtml"
Write(true);