diff --git a/HttpAbstractions.sln b/HttpAbstractions.sln index bbb59df0db..1e1b155ed3 100644 --- a/HttpAbstractions.sln +++ b/HttpAbstractions.sln @@ -49,6 +49,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SampleApp", "samples\Sample EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Html.Abstractions.Test", "test\Microsoft.AspNet.Html.Abstractions.Test\Microsoft.AspNet.Html.Abstractions.Test.xproj", "{2D187B88-94BD-4A39-AC97-F8F8B9363301}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.BufferedHtmlContent.Sources", "src\Microsoft.Framework.BufferedHtmlContent.Sources\Microsoft.Framework.BufferedHtmlContent.Sources.xproj", "{B1B2B906-24AE-4C57-AAC5-19B734014504}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Framework.BufferedHtmlContent.Test", "test\Microsoft.Framework.BufferedHtmlContent.Test\Microsoft.Framework.BufferedHtmlContent.Test.xproj", "{3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -275,6 +279,30 @@ Global {2D187B88-94BD-4A39-AC97-F8F8B9363301}.Release|Mixed Platforms.Build.0 = Release|Any CPU {2D187B88-94BD-4A39-AC97-F8F8B9363301}.Release|x86.ActiveCfg = Release|Any CPU {2D187B88-94BD-4A39-AC97-F8F8B9363301}.Release|x86.Build.0 = Release|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Debug|x86.ActiveCfg = Debug|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Debug|x86.Build.0 = Debug|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Release|Any CPU.Build.0 = Release|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Release|x86.ActiveCfg = Release|Any CPU + {B1B2B906-24AE-4C57-AAC5-19B734014504}.Release|x86.Build.0 = Release|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Debug|x86.ActiveCfg = Debug|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Debug|x86.Build.0 = Debug|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Release|Any CPU.Build.0 = Release|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Release|x86.ActiveCfg = Release|Any CPU + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -300,5 +328,7 @@ Global {68A28E4A-3ADE-4187-9625-4FF185887CB3} = {A5A15F1C-885A-452A-A731-B0173DDBD913} {1D0764B4-1DEB-4232-A714-D4B7E846918A} = {982F09D8-621E-4872-BA7B-BBDEA47D1EFD} {2D187B88-94BD-4A39-AC97-F8F8B9363301} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21} + {B1B2B906-24AE-4C57-AAC5-19B734014504} = {A5A15F1C-885A-452A-A731-B0173DDBD913} + {3E5311E2-A73E-40CC-A58C-5A62CEAD43AE} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21} EndGlobalSection EndGlobal diff --git a/src/Microsoft.Framework.BufferedHtmlContent.Sources/BufferedHtmlContent.cs b/src/Microsoft.Framework.BufferedHtmlContent.Sources/BufferedHtmlContent.cs new file mode 100644 index 0000000000..0b9ddc770f --- /dev/null +++ b/src/Microsoft.Framework.BufferedHtmlContent.Sources/BufferedHtmlContent.cs @@ -0,0 +1,125 @@ +// 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.Diagnostics; +using System.IO; +using Microsoft.AspNet.Html.Abstractions; +using Microsoft.Framework.WebEncoders; + +namespace Microsoft.Framework.Internal +{ + /// + /// Enumerable object collection which knows how to write itself. + /// + [DebuggerDisplay("{DebuggerToString()}")] + internal class BufferedHtmlContent : IHtmlContentBuilder + { + // This is not List because that would lead to wrapping all strings to IHtmlContent + // which is not space performant. + // internal for testing. + internal List Entries { get; } = new List(); + + /// + /// Appends the to the collection. + /// + /// The string to be appended. + /// A reference to this instance after the Append operation has completed. + public IHtmlContentBuilder Append(string value) + { + Entries.Add(value); + return this; + } + + /// + /// Appends a to the collection. + /// + /// The to be appended. + /// A reference to this instance after the Append operation has completed. + public IHtmlContentBuilder Append(IHtmlContent htmlContent) + { + Entries.Add(htmlContent); + return this; + } + + /// + /// Appends the HTML encoded to the collection. + /// + /// The HTML encoded string to be appended. + /// A reference to this instance after the Append operation has completed. + public IHtmlContentBuilder AppendEncoded(string value) + { + Entries.Add(new HtmlEncodedString(value)); + return this; + } + /// + /// Removes all the entries from the collection. + /// + /// A reference to this instance after the Clear operation has completed. + public IHtmlContentBuilder Clear() + { + Entries.Clear(); + return this; + } + + /// + public void WriteTo(TextWriter writer, IHtmlEncoder encoder) + { + if (writer == null) + { + throw new ArgumentNullException(nameof(writer)); + } + + if (encoder == null) + { + throw new ArgumentNullException(nameof(encoder)); + } + + foreach (var entry in Entries) + { + if (entry == null) + { + continue; + } + + var entryAsString = entry as string; + if (entryAsString != null) + { + encoder.HtmlEncode(entryAsString, writer); + } + else + { + // Only string, IHtmlContent values can be added to the buffer. + ((IHtmlContent)entry).WriteTo(writer, encoder); + } + } + } + + private string DebuggerToString() + { + using (var writer = new StringWriter()) + { + WriteTo(writer, HtmlEncoder.Default); + return writer.ToString(); + } + } + + private class HtmlEncodedString : IHtmlContent + { + public static readonly IHtmlContent NewLine = new HtmlEncodedString(Environment.NewLine); + + private readonly string _value; + + public HtmlEncodedString(string value) + { + _value = value; + } + + public void WriteTo(TextWriter writer, IHtmlEncoder encoder) + { + writer.Write(_value); + } + } + } +} diff --git a/src/Microsoft.Framework.BufferedHtmlContent.Sources/Microsoft.Framework.BufferedHtmlContent.Sources.xproj b/src/Microsoft.Framework.BufferedHtmlContent.Sources/Microsoft.Framework.BufferedHtmlContent.Sources.xproj new file mode 100644 index 0000000000..9185ec07d6 --- /dev/null +++ b/src/Microsoft.Framework.BufferedHtmlContent.Sources/Microsoft.Framework.BufferedHtmlContent.Sources.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + b1b2b906-24ae-4c57-aac5-19b734014504 + Microsoft.Framework.BufferedHtmlContent.Sources + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + \ No newline at end of file diff --git a/src/Microsoft.Framework.BufferedHtmlContent.Sources/project.json b/src/Microsoft.Framework.BufferedHtmlContent.Sources/project.json new file mode 100644 index 0000000000..6041a826dd --- /dev/null +++ b/src/Microsoft.Framework.BufferedHtmlContent.Sources/project.json @@ -0,0 +1,19 @@ +{ + "version": "1.0.0-*", + "shared": "*.cs", + "dependencies": { }, + "frameworks" : { + "net45": { }, + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "System.Runtime": "4.0.21-beta-*" + } + }, + "dotnet": { + "dependencies": { + "System.Runtime": "4.0.21-beta-*" + } + } + } +} diff --git a/test/Microsoft.Framework.BufferedHtmlContent.Test/BufferedHtmlContentTest.cs b/test/Microsoft.Framework.BufferedHtmlContent.Test/BufferedHtmlContentTest.cs new file mode 100644 index 0000000000..aa0d20b2d3 --- /dev/null +++ b/test/Microsoft.Framework.BufferedHtmlContent.Test/BufferedHtmlContentTest.cs @@ -0,0 +1,146 @@ +// 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 Microsoft.AspNet.Html.Abstractions; +using Microsoft.Framework.WebEncoders; +using Microsoft.Framework.WebEncoders.Testing; +using Xunit; + +namespace Microsoft.Framework.Internal +{ + public class BufferedHtmlContentTest + { + [Fact] + public void AppendString_AppendsAString() + { + // Arrange + var content = new BufferedHtmlContent(); + + // Act + content.Append("Hello"); + + // Assert + var result = Assert.Single(content.Entries); + Assert.IsType(result); + } + + [Fact] + public void AppendString_WrittenAsEncoded() + { + // Arrange + var content = new BufferedHtmlContent(); + content.Append("Hello"); + + var writer = new StringWriter(); + + // Act + content.WriteTo(writer, new CommonTestEncoder()); + + // Assert + Assert.Equal("HtmlEncode[[Hello]]", writer.ToString()); + } + + [Fact] + public void AppendEncoded_DoesNotGetWrittenAsEncoded() + { + // Arrange + var content = new BufferedHtmlContent(); + content.AppendEncoded("Hello"); + + var writer = new StringWriter(); + + // Act + content.WriteTo(writer, new CommonTestEncoder()); + + // Assert + Assert.Equal("Hello", writer.ToString()); + } + + [Fact] + public void AppendIHtmlContent_AppendsAsIs() + { + // Arrange + var content = new BufferedHtmlContent(); + var writer = new StringWriter(); + + // Act + content.Append(new TestHtmlContent("Hello")); + + // Assert + var result = Assert.Single(content.Entries); + var testHtmlContent = Assert.IsType(result); + testHtmlContent.WriteTo(writer, new CommonTestEncoder()); + Assert.Equal("Written from TestHtmlContent: Hello", writer.ToString()); + } + + [Fact] + public void CanAppendMultipleItems() + { + // Arrange + var content = new BufferedHtmlContent(); + + // Act + content.Append(new TestHtmlContent("hello")); + content.Append("Test"); + + // Assert + Assert.Equal(2, content.Entries.Count); + Assert.Equal("Written from TestHtmlContent: hello", content.Entries[0].ToString()); + Assert.Equal("Test", content.Entries[1]); + } + + [Fact] + public void Clear_DeletesAllItems() + { + // Arrange + var content = new BufferedHtmlContent(); + content.Append(new TestHtmlContent("hello")); + content.Append("Test"); + + // Act + content.Clear(); + + // Assert + Assert.Equal(0, content.Entries.Count); + } + + [Fact] + public void WriteTo_WritesAllItems() + { + // Arrange + var content = new BufferedHtmlContent(); + var writer = new StringWriter(); + content.Append(new TestHtmlContent("Hello")); + content.Append("Test"); + + // Act + content.WriteTo(writer, new CommonTestEncoder()); + + // Assert + Assert.Equal(2, content.Entries.Count); + Assert.Equal("Written from TestHtmlContent: HelloHtmlEncode[[Test]]", writer.ToString()); + } + + private class TestHtmlContent : IHtmlContent + { + private string _content; + + public TestHtmlContent(string content) + { + _content = content; + } + + public void WriteTo(TextWriter writer, IHtmlEncoder encoder) + { + writer.Write(ToString()); + } + + public override string ToString() + { + return "Written from TestHtmlContent: " + _content; + } + } + } +} diff --git a/test/Microsoft.Framework.BufferedHtmlContent.Test/Microsoft.Framework.BufferedHtmlContent.Test.xproj b/test/Microsoft.Framework.BufferedHtmlContent.Test/Microsoft.Framework.BufferedHtmlContent.Test.xproj new file mode 100644 index 0000000000..a112b34876 --- /dev/null +++ b/test/Microsoft.Framework.BufferedHtmlContent.Test/Microsoft.Framework.BufferedHtmlContent.Test.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 3e5311e2-a73e-40cc-a58c-5a62cead43ae + Microsoft.Framework.BufferedHtmlContent.Test + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + \ No newline at end of file diff --git a/test/Microsoft.Framework.BufferedHtmlContent.Test/project.json b/test/Microsoft.Framework.BufferedHtmlContent.Test/project.json new file mode 100644 index 0000000000..0e2b957585 --- /dev/null +++ b/test/Microsoft.Framework.BufferedHtmlContent.Test/project.json @@ -0,0 +1,18 @@ +{ + "compilationOptions": { + "warningsAsErrors": true + }, + "dependencies": { + "Microsoft.AspNet.Html.Abstractions": "1.0.0-*", + "Microsoft.Framework.BufferedHtmlContent.Sources": { "type": "build", "version": "1.0.0-*" }, + "Microsoft.Framework.WebEncoders.Testing": "1.0.0-*", + "xunit.runner.aspnet": "2.0.0-aspnet-*" + }, + "commands": { + "test": "xunit.runner.aspnet" + }, + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + } +}