From 37850b5d85c83c72607ab6e6d75f8118e3e0c855 Mon Sep 17 00:00:00 2001 From: Doug Bunting Date: Sun, 17 Jan 2016 22:34:19 -0800 Subject: [PATCH] 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 --- .../TagHelpers/TagHelperExecutionContext.cs | 69 +- .../Runtime/TagHelpers/TagHelperRunner.cs | 1 - .../TagHelpers/TagHelperScopeManager.cs | 8 +- .../TagHelpers/NullHtmlEncoder.cs | 120 +++ .../TagHelpers/TagHelperOutput.cs | 67 +- .../project.json | 5 +- .../CSharpTagHelperCodeRenderer.cs | 11 +- .../TagHelperExecutionContextTest.cs | 202 ++++- .../Runtime/TagHelpers/TagHelperOutputTest.cs | 698 --------------- .../TagHelpers/TagHelperScopeManagerTest.cs | 4 +- .../TagHelpers/NullHtmlEncoderTest.cs | 158 ++++ .../TagHelpers/TagHelperOutputTest.cs | 840 +++++++++++++++++- .../Microsoft.AspNet.Razor.Test.xproj | 3 + ...TagHelpers.CustomAttributeCodeGenerator.cs | 2 +- .../CodeGenerator/Output/BasicTagHelpers.cs | 2 +- .../CodeGenerator/Output/ComplexTagHelpers.cs | 6 +- .../Output/DynamicAttributeTagHelpers.cs | 4 +- .../CodeGenerator/Output/EscapedTagHelpers.cs | 2 +- .../Output/NestedScriptTagTagHelpers.cs | 2 +- .../PrefixedAttributeTagHelpers.Reversed.cs | 2 +- .../Output/PrefixedAttributeTagHelpers.cs | 2 +- .../Output/TagHelpersInSection.cs | 2 +- .../TagHelpersWithWeirdlySpacedAttributes.cs | 2 +- 23 files changed, 1409 insertions(+), 803 deletions(-) create mode 100644 src/Microsoft.AspNet.Razor.Runtime/TagHelpers/NullHtmlEncoder.cs delete mode 100644 test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperOutputTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/NullHtmlEncoderTest.cs diff --git a/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs b/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs index db2f474f76..d344756e22 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs @@ -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 _tagHelpers; private readonly Func _executeChildContentAsync; - private readonly Action _startTagHelperWritingScope; + private readonly Action _startTagHelperWritingScope; private readonly Func _endTagHelperWritingScope; private TagHelperContent _childContent; + private Dictionary _perEncoderChildContent; /// /// Internal for testing purposes only. @@ -28,7 +30,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers items: new Dictionary(), 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 /// s /// An identifier unique to the HTML element this context is for. /// A delegate used to execute the child content asynchronously. - /// A delegate used to start a writing scope in a Razor page. + /// + /// A delegate used to start a writing scope in a Razor page and optionally override the page's + /// within that scope. + /// /// A delegate used to end a writing scope in a Razor page. public TagHelperExecutionContext( string tagName, @@ -50,7 +55,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers IDictionary items, string uniqueId, Func executeChildContentAsync, - Action startTagHelperWritingScope, + Action startTagHelperWritingScope, Func endTagHelperWritingScope) { if (tagName == null) @@ -224,23 +229,57 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } /// - /// Execute and retrieve the rendered child content asynchronously. + /// Executes children asynchronously with the given in scope and returns their + /// rendered content. /// + /// + /// If true, multiple calls with the same will not cause children to + /// re-execute; returns cached content. + /// + /// + /// The to use when the page handles + /// non- C# expressions. If null, executes children with + /// the page's current . + /// /// A that on completion returns the rendered child content. - /// - /// Child content is only executed once. Successive calls to this method or successive executions of the - /// returned return a cached result. - /// - public async Task GetChildContentAsync(bool useCachedResult) + public async Task 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(); + } + 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); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperRunner.cs b/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperRunner.cs index 8bc8d6ca18..a2e814c23b 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperRunner.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperRunner.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Razor.TagHelpers; diff --git a/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperScopeManager.cs b/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperScopeManager.cs index f2ae450871..9716b05cf2 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperScopeManager.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/Runtime/TagHelpers/TagHelperScopeManager.cs @@ -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 /// HTML syntax of the element in the Razor source. /// An identifier unique to the HTML element this scope is for. /// A delegate used to execute the child content asynchronously. - /// A delegate used to start a writing scope in a Razor page. + /// + /// A delegate used to start a writing scope in a Razor page and optionally override the page's + /// within that scope. + /// /// A delegate used to end a writing scope in a Razor page. /// A to use. public TagHelperExecutionContext Begin( @@ -39,7 +43,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers TagMode tagMode, string uniqueId, Func executeChildContentAsync, - Action startTagHelperWritingScope, + Action startTagHelperWritingScope, Func endTagHelperWritingScope) { if (tagName == null) diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/NullHtmlEncoder.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/NullHtmlEncoder.cs new file mode 100644 index 0000000000..f3b0637e4b --- /dev/null +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/NullHtmlEncoder.cs @@ -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 +{ + /// + /// A that does not encode. Should not be used when writing directly to a response + /// expected to contain valid HTML. + /// + public class NullHtmlEncoder : HtmlEncoder + { + /// + /// Initializes a instance. + /// + protected NullHtmlEncoder() + { + } + + /// + /// A instance that does not encode. Should not be used when writing directly to a + /// response expected to contain valid HTML. + /// + public static new NullHtmlEncoder Default { get; } = new NullHtmlEncoder(); + + /// + public override int MaxOutputCharactersPerInputCharacter + { + get + { + return 1; + } + } + + /// + public override string Encode(string value) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + return value; + } + + /// + 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); + } + + /// + 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)); + } + + /// + public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) + { + return -1; + } + + /// + 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; + } + + /// + public override bool WillEncode(int unicodeScalar) + { + return false; + } + } +} diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs index cbfa2a1baa..35fcdd2780 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// public class TagHelperOutput : IHtmlContent { - private readonly Func> _getChildContentAsync; + private readonly Func> _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(new DefaultTagHelperContent())) + (useCachedResult, encoder) => Task.FromResult(new DefaultTagHelperContent())) { } @@ -36,12 +36,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// /// The HTML element's tag name. /// The HTML attributes. - /// A delegate used to execute and retrieve the rendered child content - /// asynchronously. + /// + /// A delegate used to execute children asynchronously with the given in scope and + /// return their rendered content. + /// public TagHelperOutput( string tagName, TagHelperAttributeList attributes, - Func> getChildContentAsync) + Func> getChildContentAsync) { if (attributes == null) { @@ -206,24 +208,65 @@ namespace Microsoft.AspNet.Razor.TagHelpers } /// - /// A delegate used to execute children asynchronously. + /// Executes children asynchronously and returns their rendered content. /// /// A that on completion returns content rendered by children. - /// This method is memoized. + /// + /// This method is memoized. Multiple calls will not cause children to re-execute with the page's original + /// . + /// public Task GetChildContentAsync() { - return GetChildContentAsync(useCachedResult: true); + return GetChildContentAsync(useCachedResult: true, encoder: null); } /// - /// A delegate used to execute children asynchronously. + /// Executes children asynchronously and returns their rendered content. /// - /// If true multiple calls to this method will not cause re-execution - /// of child content; cached content will be returned. + /// + /// If true, multiple calls will not cause children to re-execute with the page's original + /// ; returns cached content. + /// /// A that on completion returns content rendered by children. public Task GetChildContentAsync(bool useCachedResult) { - return _getChildContentAsync(useCachedResult); + return GetChildContentAsync(useCachedResult, encoder: null); + } + + /// + /// Executes children asynchronously with the given in scope and returns their + /// rendered content. + /// + /// + /// The to use when the page handles non- C# expressions. + /// If null, executes children with the page's current . + /// + /// A that on completion returns content rendered by children. + /// + /// This method is memoized. Multiple calls with the same instance will not cause + /// children to re-execute with that encoder in scope. + /// + public Task GetChildContentAsync(HtmlEncoder encoder) + { + return GetChildContentAsync(useCachedResult: true, encoder: encoder); + } + + /// + /// Executes children asynchronously with the given in scope and returns their + /// rendered content. + /// + /// + /// If true, multiple calls with the same will not cause children to + /// re-execute; returns cached content. + /// + /// + /// The to use when the page handles non- C# expressions. + /// If null, executes children with the page's current . + /// + /// A that on completion returns content rendered by children. + public Task GetChildContentAsync(bool useCachedResult, HtmlEncoder encoder) + { + return _getChildContentAsync(useCachedResult, encoder); } /// diff --git a/src/Microsoft.AspNet.Razor.Runtime/project.json b/src/Microsoft.AspNet.Razor.Runtime/project.json index 1c8f2f4d7e..5979656201 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/project.json +++ b/src/Microsoft.AspNet.Razor.Runtime/project.json @@ -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", diff --git a/src/Microsoft.AspNet.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs b/src/Microsoft.AspNet.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs index bf46fbb6b7..a11eb7790d 100644 --- a/src/Microsoft.AspNet.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs +++ b/src/Microsoft.AspNet.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs @@ -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); } } diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperExecutionContextTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperExecutionContextTest.cs index 056b148dd1..4c017fade1 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperExecutionContextTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperExecutionContextTest.cs @@ -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 HtmlEncoderData + { + get + { + return new TheoryData + { + null, + HtmlEncoder.Default, + NullHtmlEncoder.Default, + new HtmlTestEncoder(), + }; + } + } + + public static TheoryData HtmlEncoderDataLessNull + { + get + { + var data = new TheoryData(); + 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(), + 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(), + 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(), + 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(), + 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(), 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 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 diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperOutputTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperOutputTest.cs deleted file mode 100644 index f3427feabd..0000000000 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperOutputTest.cs +++ /dev/null @@ -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 WriteTagHelper_InputData - { - get - { - // parameters: TagHelperOutput, expectedOutput - return new TheoryData - { - { - // 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), - "
Hello World!
" - }, - { - 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), - "

Hello World!

" - }, - { - GetTagHelperOutput( - tagName: "p", - attributes: new TagHelperAttributeList() - { - { "test", "testVal" }, - { "something", " spaced " } - }, - tagMode: TagMode.StartTagAndEndTag, - preElement: null, - preContent: null, - content: "Hello World!", - postContent: null, - postElement: null), - "

Hello World!

" - }, - { - 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), - "

Hello World!

" - }, - { - 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), - "

Hello World!

" - }, - { - 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), - "

Hello World!

" - }, - { - 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), - "

Hello World!

" - }, - { - GetTagHelperOutput( - tagName: "p", - attributes: new TagHelperAttributeList() { { "test", "testVal" } }, - tagMode: TagMode.SelfClosing, - preElement: null, - preContent: null, - content: "Hello World!", - postContent: null, - postElement: null), - "

" - }, - { - GetTagHelperOutput( - tagName: "p", - attributes: new TagHelperAttributeList() - { - { "test", "testVal" }, - { "something", " spaced " } - }, - tagMode: TagMode.SelfClosing, - preElement: null, - preContent: null, - content: "Hello World!", - postContent: null, - postElement: null), - "

" - }, - { - GetTagHelperOutput( - tagName: "p", - attributes: new TagHelperAttributeList() { { "test", "testVal" } }, - tagMode: TagMode.StartTagOnly, - preElement: null, - preContent: null, - content: "Hello World!", - postContent: null, - postElement: null), - "

" - }, - { - GetTagHelperOutput( - tagName: "p", - attributes: new TagHelperAttributeList() - { - { "test", "testVal" }, - { "something", " spaced " } - }, - tagMode: TagMode.StartTagOnly, - preElement: null, - preContent: null, - content: "Hello World!", - postContent: null, - postElement: null), - "

" - }, - { - GetTagHelperOutput( - tagName: "p", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.StartTagAndEndTag, - preElement: null, - preContent: "Hello World!", - content: null, - postContent: null, - postElement: null), - "

Hello World!

" - }, - { - GetTagHelperOutput( - tagName: "p", - 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(), - tagMode: TagMode.StartTagAndEndTag, - preElement: null, - preContent: null, - content: null, - postContent: "Hello World!", - postElement: null), - "

Hello World!

" - }, - { - GetTagHelperOutput( - tagName: "p", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.StartTagAndEndTag, - preElement: null, - preContent: "Hello", - content: "Test", - postContent: "World!", - postElement: null), - "

HelloTestWorld!

" - }, - { - GetTagHelperOutput( - tagName: "p", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.SelfClosing, - preElement: null, - preContent: "Hello", - content: "Test", - postContent: "World!", - postElement: null), - "

" - }, - { - GetTagHelperOutput( - tagName: "p", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.StartTagOnly, - preElement: null, - preContent: "Hello", - content: "Test", - postContent: "World!", - postElement: null), - "

" - }, - { - GetTagHelperOutput( - tagName: "custom", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.StartTagAndEndTag, - preElement: null, - preContent: "Hello", - content: "Test", - postContent: "World!", - postElement: null), - "HelloTestWorld!" - }, - { - GetTagHelperOutput( - tagName: "random", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.SelfClosing, - preElement: null, - preContent: "Hello", - content: "Test", - postContent: "World!", - postElement: null), - "" - }, - { - GetTagHelperOutput( - tagName: "random", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.StartTagOnly, - preElement: null, - preContent: "Hello", - content: "Test", - postContent: "World!", - postElement: null), - "" - }, - { - GetTagHelperOutput( - tagName: "custom", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.StartTagAndEndTag, - preElement: "Before", - preContent: null, - content: null, - postContent: null, - postElement: null), - "Before" - }, - { - 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" - }, - { - GetTagHelperOutput( - tagName: "custom", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.SelfClosing, - preElement: "Before", - preContent: null, - content: null, - postContent: null, - postElement: null), - "Before" - }, - { - 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" - }, - { - GetTagHelperOutput( - tagName: "custom", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.StartTagOnly, - preElement: "Before", - preContent: null, - content: null, - postContent: null, - postElement: null), - "Before" - }, - { - GetTagHelperOutput( - tagName: "custom", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.StartTagAndEndTag, - preElement: null, - preContent: null, - content: null, - postContent: null, - postElement: "After"), - "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"), - "After" - }, - { - GetTagHelperOutput( - tagName: "custom", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.SelfClosing, - preElement: null, - preContent: null, - content: null, - postContent: null, - postElement: "After"), - "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"), - "After" - }, - { - GetTagHelperOutput( - tagName: "custom", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.StartTagOnly, - preElement: null, - preContent: null, - content: null, - postContent: null, - postElement: "After"), - "After" - }, - { - GetTagHelperOutput( - tagName: "custom", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.StartTagAndEndTag, - preElement: "Before", - preContent: "Hello", - content: "Test", - postContent: "World!", - postElement: "After"), - "BeforeHelloTestWorld!After" - }, - { - GetTagHelperOutput( - tagName: "custom", - attributes: new TagHelperAttributeList { { "test", "testVal" } }, - tagMode: TagMode.StartTagAndEndTag, - preElement: "Before", - preContent: "Hello", - content: "Test", - postContent: "World!", - postElement: "After"), - "BeforeHelloTestWorld!After" - }, - { - GetTagHelperOutput( - tagName: "custom", - attributes: new TagHelperAttributeList(), - tagMode: TagMode.SelfClosing, - preElement: "Before", - preContent: "Hello", - content: "Test", - postContent: "World!", - postElement: "After"), - "BeforeAfter" - }, - { - 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"), - "BeforeAfter" - }, - { - 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(), - 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(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; - } - } -} diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperScopeManagerTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperScopeManagerTest.cs index f70504271a..8253b2527f 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperScopeManagerTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperScopeManagerTest.cs @@ -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()); } } diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/NullHtmlEncoderTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/NullHtmlEncoderTest.cs new file mode 100644 index 0000000000..f20509d1cc --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/NullHtmlEncoderTest.cs @@ -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); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs index 2125030590..2f1cfdbca2 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs @@ -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(new DefaultTagHelperContent()); + passedUseCacheResult = useCachedResult; + passedEncoder = encoder; + return Task.FromResult(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 HtmlEncoderData + { + get + { + return new TheoryData + { + 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(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(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 UseCachedResultAndHtmlEncoderData + { + get + { + var data = new TheoryData(); + 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(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(new DefaultTagHelperContent())); + (cachedResult, encoder) => Task.FromResult(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(new DefaultTagHelperContent())); + (cachedResult, encoder) => Task.FromResult(new DefaultTagHelperContent())); // Act tagHelperOutput.Attributes[updateName] = "super button"; @@ -226,5 +354,687 @@ namespace Microsoft.AspNet.Razor.TagHelpers attribute, CaseSensitiveTagHelperAttributeComparer.Default); } + + public static TheoryData WriteTagHelper_InputData + { + get + { + // parameters: TagHelperOutput, expectedOutput + return new TheoryData + { + { + // 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), + "

Hello World!
" + }, + { + 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), + "

Hello World!

" + }, + { + GetTagHelperOutput( + tagName: "p", + attributes: new TagHelperAttributeList() + { + { "test", "testVal" }, + { "something", " spaced " } + }, + tagMode: TagMode.StartTagAndEndTag, + preElement: null, + preContent: null, + content: "Hello World!", + postContent: null, + postElement: null), + "

Hello World!

" + }, + { + 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), + "

Hello World!

" + }, + { + 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), + "

Hello World!

" + }, + { + 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), + "

Hello World!

" + }, + { + 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), + "

Hello World!

" + }, + { + GetTagHelperOutput( + tagName: "p", + attributes: new TagHelperAttributeList() { { "test", "testVal" } }, + tagMode: TagMode.SelfClosing, + preElement: null, + preContent: null, + content: "Hello World!", + postContent: null, + postElement: null), + "

" + }, + { + GetTagHelperOutput( + tagName: "p", + attributes: new TagHelperAttributeList() + { + { "test", "testVal" }, + { "something", " spaced " } + }, + tagMode: TagMode.SelfClosing, + preElement: null, + preContent: null, + content: "Hello World!", + postContent: null, + postElement: null), + "

" + }, + { + GetTagHelperOutput( + tagName: "p", + attributes: new TagHelperAttributeList() { { "test", "testVal" } }, + tagMode: TagMode.StartTagOnly, + preElement: null, + preContent: null, + content: "Hello World!", + postContent: null, + postElement: null), + "

" + }, + { + GetTagHelperOutput( + tagName: "p", + attributes: new TagHelperAttributeList() + { + { "test", "testVal" }, + { "something", " spaced " } + }, + tagMode: TagMode.StartTagOnly, + preElement: null, + preContent: null, + content: "Hello World!", + postContent: null, + postElement: null), + "

" + }, + { + GetTagHelperOutput( + tagName: "p", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.StartTagAndEndTag, + preElement: null, + preContent: "Hello World!", + content: null, + postContent: null, + postElement: null), + "

Hello World!

" + }, + { + GetTagHelperOutput( + tagName: "p", + 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(), + tagMode: TagMode.StartTagAndEndTag, + preElement: null, + preContent: null, + content: null, + postContent: "Hello World!", + postElement: null), + "

Hello World!

" + }, + { + GetTagHelperOutput( + tagName: "p", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.StartTagAndEndTag, + preElement: null, + preContent: "Hello", + content: "Test", + postContent: "World!", + postElement: null), + "

HelloTestWorld!

" + }, + { + GetTagHelperOutput( + tagName: "p", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.SelfClosing, + preElement: null, + preContent: "Hello", + content: "Test", + postContent: "World!", + postElement: null), + "

" + }, + { + GetTagHelperOutput( + tagName: "p", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.StartTagOnly, + preElement: null, + preContent: "Hello", + content: "Test", + postContent: "World!", + postElement: null), + "

" + }, + { + GetTagHelperOutput( + tagName: "custom", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.StartTagAndEndTag, + preElement: null, + preContent: "Hello", + content: "Test", + postContent: "World!", + postElement: null), + "HelloTestWorld!" + }, + { + GetTagHelperOutput( + tagName: "random", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.SelfClosing, + preElement: null, + preContent: "Hello", + content: "Test", + postContent: "World!", + postElement: null), + "" + }, + { + GetTagHelperOutput( + tagName: "random", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.StartTagOnly, + preElement: null, + preContent: "Hello", + content: "Test", + postContent: "World!", + postElement: null), + "" + }, + { + GetTagHelperOutput( + tagName: "custom", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.StartTagAndEndTag, + preElement: "Before", + preContent: null, + content: null, + postContent: null, + postElement: null), + "Before" + }, + { + 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" + }, + { + GetTagHelperOutput( + tagName: "custom", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.SelfClosing, + preElement: "Before", + preContent: null, + content: null, + postContent: null, + postElement: null), + "Before" + }, + { + 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" + }, + { + GetTagHelperOutput( + tagName: "custom", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.StartTagOnly, + preElement: "Before", + preContent: null, + content: null, + postContent: null, + postElement: null), + "Before" + }, + { + GetTagHelperOutput( + tagName: "custom", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.StartTagAndEndTag, + preElement: null, + preContent: null, + content: null, + postContent: null, + postElement: "After"), + "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"), + "After" + }, + { + GetTagHelperOutput( + tagName: "custom", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.SelfClosing, + preElement: null, + preContent: null, + content: null, + postContent: null, + postElement: "After"), + "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"), + "After" + }, + { + GetTagHelperOutput( + tagName: "custom", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.StartTagOnly, + preElement: null, + preContent: null, + content: null, + postContent: null, + postElement: "After"), + "After" + }, + { + GetTagHelperOutput( + tagName: "custom", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.StartTagAndEndTag, + preElement: "Before", + preContent: "Hello", + content: "Test", + postContent: "World!", + postElement: "After"), + "BeforeHelloTestWorld!After" + }, + { + GetTagHelperOutput( + tagName: "custom", + attributes: new TagHelperAttributeList { { "test", "testVal" } }, + tagMode: TagMode.StartTagAndEndTag, + preElement: "Before", + preContent: "Hello", + content: "Test", + postContent: "World!", + postElement: "After"), + "BeforeHelloTestWorld!After" + }, + { + GetTagHelperOutput( + tagName: "custom", + attributes: new TagHelperAttributeList(), + tagMode: TagMode.SelfClosing, + preElement: "Before", + preContent: "Hello", + content: "Test", + postContent: "World!", + postElement: "After"), + "BeforeAfter" + }, + { + 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"), + "BeforeAfter" + }, + { + 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(), + 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( + 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; + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/Microsoft.AspNet.Razor.Test.xproj b/test/Microsoft.AspNet.Razor.Test/Microsoft.AspNet.Razor.Test.xproj index dbd89e900d..acf9a82bf4 100644 --- a/test/Microsoft.AspNet.Razor.Test/Microsoft.AspNet.Razor.Test.xproj +++ b/test/Microsoft.AspNet.Razor.Test/Microsoft.AspNet.Razor.Test.xproj @@ -16,5 +16,8 @@ + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs index 1c4430494a..eb7c6233f3 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs @@ -51,7 +51,7 @@ namespace TestOutput __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper); __TestNamespace_InputTagHelper2 = CreateTagHelper(); __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2); - StartTagHelperWritingScope(); + StartTagHelperWritingScope(null); WriteLiteral("2000 + "); #line 6 "BasicTagHelpers.cshtml" Write(ViewBag.DefaultInterval); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/BasicTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/BasicTagHelpers.cs index dcd6c51ea8..5c53c9a0f2 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/BasicTagHelpers.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/BasicTagHelpers.cs @@ -52,7 +52,7 @@ namespace TestOutput __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper); __TestNamespace_InputTagHelper2 = CreateTagHelper(); __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2); - StartTagHelperWritingScope(); + StartTagHelperWritingScope(null); WriteLiteral("2000 + "); #line 6 "BasicTagHelpers.cshtml" Write(ViewBag.DefaultInterval); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/ComplexTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/ComplexTagHelpers.cs index 1f4bf429d9..67610018c9 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/ComplexTagHelpers.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/ComplexTagHelpers.cs @@ -119,7 +119,7 @@ namespace TestOutput __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper); __TestNamespace_InputTagHelper2 = CreateTagHelper(); __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(); __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(); __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2); - StartTagHelperWritingScope(); + StartTagHelperWritingScope(null); #line 18 "ComplexTagHelpers.cshtml" if(true) { diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/DynamicAttributeTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/DynamicAttributeTagHelpers.cs index 5643b91a46..aa2a16236e 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/DynamicAttributeTagHelpers.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/DynamicAttributeTagHelpers.cs @@ -103,7 +103,7 @@ WriteTo(__razor_attribute_value_writer, string.Empty); , StartTagHelperWritingScope, EndTagHelperWritingScope); __TestNamespace_InputTagHelper = CreateTagHelper(); __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(); __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper); - StartTagHelperWritingScope(); + StartTagHelperWritingScope(null); #line 9 "DynamicAttributeTagHelpers.cshtml" WriteLiteral(long.MinValue); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/EscapedTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/EscapedTagHelpers.cs index 6bc9d4bcc7..90d8aaf0b8 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/EscapedTagHelpers.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/EscapedTagHelpers.cs @@ -44,7 +44,7 @@ namespace TestOutput __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper); __TestNamespace_InputTagHelper2 = CreateTagHelper(); __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2); - StartTagHelperWritingScope(); + StartTagHelperWritingScope(null); #line 6 "EscapedTagHelpers.cshtml" WriteLiteral(DateTime.Now); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/NestedScriptTagTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/NestedScriptTagTagHelpers.cs index 3399b2b1d5..efca9ee960 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/NestedScriptTagTagHelpers.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/NestedScriptTagTagHelpers.cs @@ -54,7 +54,7 @@ namespace TestOutput __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper); __TestNamespace_InputTagHelper2 = CreateTagHelper(); __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper2); - StartTagHelperWritingScope(); + StartTagHelperWritingScope(null); WriteLiteral("2000 + "); #line 8 "NestedScriptTagTagHelpers.cshtml" Write(ViewBag.DefaultInterval); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/PrefixedAttributeTagHelpers.Reversed.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/PrefixedAttributeTagHelpers.Reversed.cs index e7178d8cc8..46dbf6b84b 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/PrefixedAttributeTagHelpers.Reversed.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/PrefixedAttributeTagHelpers.Reversed.cs @@ -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); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/PrefixedAttributeTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/PrefixedAttributeTagHelpers.cs index 2173752712..265401b5dd 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/PrefixedAttributeTagHelpers.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/PrefixedAttributeTagHelpers.cs @@ -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); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersInSection.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersInSection.cs index 189924185d..6a38385dcd 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersInSection.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersInSection.cs @@ -76,7 +76,7 @@ namespace TestOutput , StartTagHelperWritingScope, EndTagHelperWritingScope); __TestNamespace_MyTagHelper = CreateTagHelper(); __tagHelperExecutionContext.Add(__TestNamespace_MyTagHelper); - StartTagHelperWritingScope(); + StartTagHelperWritingScope(null); WriteLiteral("Current Time: "); #line 9 "TagHelpersInSection.cshtml" WriteLiteral(DateTime.Now); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersWithWeirdlySpacedAttributes.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersWithWeirdlySpacedAttributes.cs index 10cb8280d2..284a47d1d6 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersWithWeirdlySpacedAttributes.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersWithWeirdlySpacedAttributes.cs @@ -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);