public void Ctor_WithUnicodeRanges()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.Latin1Supplement, UnicodeRanges.MiscellaneousSymbols);

            // Act & assert
            Assert.Equal("a", encoder.HtmlEncode("a"));
            Assert.Equal("\u00E9", encoder.HtmlEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */));
            Assert.Equal("\u2601", encoder.HtmlEncode("\u2601" /* CLOUD */));
        }
示例#2
0
        public void Ctor_WithNoParameters_DefaultsToBasicLatin()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder();

            // Act & assert
            Assert.Equal("a", encoder.HtmlEncode("a"));
            Assert.Equal("é", encoder.HtmlEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */));
            Assert.Equal("☁", encoder.HtmlEncode("\u2601" /* CLOUD */));
        }
        public void Ctor_WithNoParameters_DefaultsToBasicLatin()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder();

            // Act & assert
            Assert.Equal("a", encoder.HtmlEncode("a"));
            Assert.Equal("é", encoder.HtmlEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */));
            Assert.Equal("☁", encoder.HtmlEncode("\u2601" /* CLOUD */));
        }
示例#4
0
        public void Ctor_WithUnicodeRanges()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.Latin1Supplement, UnicodeRanges.MiscellaneousSymbols);

            // Act & assert
            Assert.Equal("a", encoder.HtmlEncode("a"));
            Assert.Equal("\u00E9", encoder.HtmlEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */));
            Assert.Equal("\u2601", encoder.HtmlEncode("\u2601" /* CLOUD */));
        }
示例#5
0
        public void HtmlEncode_NullInput_Throws()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder();

            Assert.Throws <ArgumentNullException>(() => { encoder.HtmlEncode(null); });
        }
示例#6
0
        public void HtmlEncode_EmptyStringInput_ReturnsEmptyString()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder();

            // Act & assert
            Assert.Equal("", encoder.HtmlEncode(""));
        }
示例#7
0
        public void HtmlEncode_NullInput_ReturnsNull()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder();

            // Act & assert
            Assert.Null(encoder.HtmlEncode(null));
        }
示例#8
0
        public void HtmlEncode_InputDoesNotRequireEncoding_ReturnsOriginalStringInstance()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder();
            string      input   = "Hello, there!";

            // Act & assert
            Assert.Same(input, encoder.HtmlEncode(input));
        }
示例#9
0
        public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple(string input, string expected)
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All);

            // Act
            string retVal = encoder.HtmlEncode(input);

            // Assert
            Assert.Equal(expected, retVal);
        }
示例#10
0
        public void HtmlEncode_StringSubstring()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder();
            var         output  = new StringWriter();

            // Act
            encoder.HtmlEncode("Hello+world!", 3, 5, output);

            // Assert
            Assert.Equal("lo&#x2B;wo", output.ToString());
        }
        public void HtmlEncode_PositiveTestCase()
        {
            // Arrange
            IHtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All);
            StringWriter writer = new StringWriter();

            // Act
            encoder.HtmlEncode("Hello+there!", writer);

            // Assert
            Assert.Equal("Hello&#x2B;there!", writer.ToString());
        }
示例#12
0
        public void HtmlEncode_PositiveTestCase()
        {
            // Arrange
            IHtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All);
            StringWriter writer  = new StringWriter();

            // Act
            encoder.HtmlEncode("Hello+there!", writer);

            // Assert
            Assert.Equal("Hello&#x2B;there!", writer.ToString());
        }
示例#13
0
        public void HtmlEncode_BadSurrogates_ReturnsUnicodeReplacementChar()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All); // allow all codepoints

            // "a<unpaired leading>b<unpaired trailing>c<trailing before leading>d<unpaired trailing><valid>e<high at end of string>"
            const string input    = "a\uD800b\uDFFFc\uDFFF\uD800d\uDFFF\uD800\uDFFFe\uD800";
            const string expected = "a\uFFFDb\uFFFDc\uFFFD\uFFFDd\uFFFD&#x103FF;e\uFFFD";

            // Act
            string retVal = encoder.HtmlEncode(input);

            // Assert
            Assert.Equal(expected, retVal);
        }
        public void Default_EquivalentToBasicLatin()
        {
            // Arrange
            HtmlEncoder controlEncoder = new HtmlEncoder(UnicodeRanges.BasicLatin);
            HtmlEncoder testEncoder = HtmlEncoder.Default;

            // Act & assert
            for (int i = 0; i <= Char.MaxValue; i++)
            {
                if (!IsSurrogateCodePoint(i))
                {
                    string input = new String((char)i, 1);
                    Assert.Equal(controlEncoder.HtmlEncode(input), testEncoder.HtmlEncode(input));
                }
            }
        }
示例#15
0
        public void Default_EquivalentToBasicLatin()
        {
            // Arrange
            HtmlEncoder controlEncoder = new HtmlEncoder(UnicodeRanges.BasicLatin);
            HtmlEncoder testEncoder    = HtmlEncoder.Default;

            // Act & assert
            for (int i = 0; i <= char.MaxValue; i++)
            {
                if (!IsSurrogateCodePoint(i))
                {
                    string input = new string((char)i, 1);
                    Assert.Equal(controlEncoder.HtmlEncode(input), testEncoder.HtmlEncode(input));
                }
            }
        }
示例#16
0
        public void Ctor_WithCodePointFilter()
        {
            // Arrange
            var         filter  = new CodePointFilter().AllowChars("ab").AllowChars('\0', '&', '\uFFFF', 'd');
            HtmlEncoder encoder = new HtmlEncoder(filter);

            // Act & assert
            Assert.Equal("a", encoder.HtmlEncode("a"));
            Assert.Equal("b", encoder.HtmlEncode("b"));
            Assert.Equal("&#x63;", encoder.HtmlEncode("c"));
            Assert.Equal("d", encoder.HtmlEncode("d"));
            Assert.Equal("&#x0;", encoder.HtmlEncode("\0"));        // we still always encode control chars
            Assert.Equal("&amp;", encoder.HtmlEncode("&"));         // we still always encode HTML-special chars
            Assert.Equal("&#xFFFF;", encoder.HtmlEncode("\uFFFF")); // we still always encode non-chars and other forbidden chars
        }
        public void Ctor_WithCodePointFilter()
        {
            // Arrange
            var filter = new CodePointFilter().AllowChars("ab").AllowChars('\0', '&', '\uFFFF', 'd');
            HtmlEncoder encoder = new HtmlEncoder(filter);

            // Act & assert
            Assert.Equal("a", encoder.HtmlEncode("a"));
            Assert.Equal("b", encoder.HtmlEncode("b"));
            Assert.Equal("&#x63;", encoder.HtmlEncode("c"));
            Assert.Equal("d", encoder.HtmlEncode("d"));
            Assert.Equal("&#x0;", encoder.HtmlEncode("\0")); // we still always encode control chars
            Assert.Equal("&amp;", encoder.HtmlEncode("&")); // we still always encode HTML-special chars
            Assert.Equal("&#xFFFF;", encoder.HtmlEncode("\uFFFF")); // we still always encode non-chars and other forbidden chars
        }
示例#18
0
        public void JavaScriptStringEncode_DoesNotOutputHtmlSensitiveCharacters()
        {
            // Per the design document, we provide additional defense-in-depth
            // by never emitting HTML-sensitive characters unescaped.

            // Arrange
            JavaScriptStringEncoder javaScriptStringEncoder = new JavaScriptStringEncoder(UnicodeRanges.All);
            HtmlEncoder             htmlEncoder             = new HtmlEncoder(UnicodeRanges.All);

            // Act & assert
            for (int i = 0; i <= 0x10FFFF; i++)
            {
                if (IsSurrogateCodePoint(i))
                {
                    continue; // surrogates don't matter here
                }

                string javaScriptStringEncoded = javaScriptStringEncoder.JavaScriptStringEncode(Char.ConvertFromUtf32(i));
                string thenHtmlEncoded         = htmlEncoder.HtmlEncode(javaScriptStringEncoded);
                Assert.Equal(javaScriptStringEncoded, thenHtmlEncoded); // should have contained no HTML-sensitive characters
            }
        }
示例#19
0
        public void UrlEncode_DoesNotOutputHtmlSensitiveCharacters()
        {
            // Per the design document, we provide additional defense-in-depth
            // by never emitting HTML-sensitive characters unescaped.

            // Arrange
            UrlEncoder urlEncoder = new UrlEncoder(UnicodeRanges.All);
            HtmlEncoder htmlEncoder = new HtmlEncoder(UnicodeRanges.All);

            // Act & assert
            for (int i = 0; i <= 0x10FFFF; i++)
            {
                if (IsSurrogateCodePoint(i))
                {
                    continue; // surrogates don't matter here
                }

                string urlEncoded = urlEncoder.UrlEncode(Char.ConvertFromUtf32(i));
                string thenHtmlEncoded = htmlEncoder.HtmlEncode(urlEncoded);
                Assert.Equal(urlEncoded, thenHtmlEncoded); // should have contained no HTML-sensitive characters
            }
        }
示例#20
0
        public async Task RenderAsync_AsPartial_ExecutesLayout_ButNotViewStartPages()
        {
            // Arrange
            var htmlEncoder = new HtmlEncoder();
            var expected = string.Join(htmlEncoder.HtmlEncode(Environment.NewLine),
                                       "layout-content",
                                       "page-content");
            var page = new TestableRazorPage(v =>
            {
                v.HtmlEncoder = htmlEncoder;
                v.Layout = LayoutPath;
                v.Write("page-content");
            });

            var layout = new TestableRazorPage(v =>
            {
                v.HtmlEncoder = htmlEncoder;
                v.Write("layout-content" + Environment.NewLine);
                v.RenderBodyPublic();
            });
            var pageFactory = new Mock<IRazorPageFactory>();
            pageFactory.Setup(p => p.CreateInstance(LayoutPath))
                       .Returns(layout);

            var viewEngine = new Mock<IRazorViewEngine>();
            viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath))
                      .Returns(new RazorPageResult(LayoutPath, layout));

            var viewStartProvider = CreateViewStartProvider();
            var view = new RazorView(viewEngine.Object,
                                     Mock.Of<IRazorPageActivator>(),
                                     viewStartProvider,
                                     page,
                                     isPartial: true);
            var viewContext = CreateViewContext(view);

            // Act
            await view.RenderAsync(viewContext);

            // Assert
            Mock.Get(viewStartProvider)
                .Verify(v => v.GetViewStartPages(It.IsAny<string>()), Times.Never());
            Assert.Equal(expected, viewContext.Writer.ToString());
        }
示例#21
0
        public async Task RenderAsync_WithNestedSections_ThrowsIfSectionsWereDefinedButNotRendered()
        {
            // Arrange
            var htmlEncoder = new HtmlEncoder();
            var page = new TestableRazorPage(v =>
            {
                v.HtmlEncoder = htmlEncoder;
                v.Layout = "~/Shared/Layout1.cshtml";
                v.WriteLiteral("BodyContent");
                v.DefineSection("foo", async writer =>
                {
                    await writer.WriteLineAsync("foo-content");
                });
            });
            var nestedLayout = new TestableRazorPage(v =>
            {
                v.HtmlEncoder = htmlEncoder;
                v.Layout = "~/Shared/Layout2.cshtml";
                v.Write("NestedLayout" + Environment.NewLine);
                v.RenderBodyPublic();
                v.DefineSection("foo", async writer =>
                {
                    await writer.WriteLineAsync(htmlEncoder.HtmlEncode(v.RenderSection("foo").ToString()));
                });
            });
            var baseLayout = new TestableRazorPage(v =>
            {
                v.HtmlEncoder = htmlEncoder;
                v.Write("BaseLayout" + Environment.NewLine);
                v.RenderBodyPublic();
            });

            var viewEngine = new Mock<IRazorViewEngine>();
            viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout1.cshtml"))
                       .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
            viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout2.cshtml"))
                       .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout));

            var view = new RazorView(viewEngine.Object,
                                     Mock.Of<IRazorPageActivator>(),
                                     CreateViewStartProvider(),
                                     page,
                                     isPartial: false);
            var viewContext = CreateViewContext(view);

            // Act and Assert
            var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => view.RenderAsync(viewContext));
            Assert.Equal("The following sections have been defined but have not been rendered: 'foo'.", ex.Message);
        }
        public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple(string input, string expected)
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All);

            // Act
            string retVal = encoder.HtmlEncode(input);

            // Assert
            Assert.Equal(expected, retVal);
        }
示例#23
0
        public async Task FlushAsync_DoesNotThrowWhenInvokedInsideOfASection()
        {
            // Arrange
            var htmlEncoder = new HtmlEncoder();
            var expected = "layout-1" +
                           htmlEncoder.HtmlEncode(Environment.NewLine) +
                           "section-content-1" +
                           Environment.NewLine +
                           "section-content-2";

            var page = new TestableRazorPage(v =>
           {
               v.HtmlEncoder = htmlEncoder;
               v.Layout = "layout-1";
               v.DefineSection("foo", async _ =>
               {
                   v.WriteLiteral("section-content-1" + Environment.NewLine);
                   await v.FlushAsync();
                   v.WriteLiteral("section-content-2");
               });
           });

            var layout1 = new TestableRazorPage(v =>
            {
                v.HtmlEncoder = htmlEncoder;
                v.Write("layout-1" + Environment.NewLine);
                v.RenderBodyPublic();
                v.Write(v.RenderSection("foo"));
            });

            var viewEngine = new Mock<IRazorViewEngine>();
            viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "layout-1"))
                       .Returns(new RazorPageResult("layout-1", layout1));

            var view = new RazorView(viewEngine.Object,
                                     Mock.Of<IRazorPageActivator>(),
                                     CreateViewStartProvider(),
                                     page,
                                     isPartial: false);
            var viewContext = CreateViewContext(view);

            // Act
            await view.RenderAsync(viewContext);

            // Assert
            Assert.Equal(expected, viewContext.Writer.ToString());
        }
示例#24
0
        public async Task RenderAsync_ExecutesNestedLayoutsWithNestedSections()
        {
            // Arrange
            var htmlEncoder = new HtmlEncoder();
            var htmlEncodedNewLine = htmlEncoder.HtmlEncode(Environment.NewLine);
            var expected = "BaseLayout" +
                           htmlEncodedNewLine +
                           "NestedLayout" +
                           htmlEncodedNewLine +
                           "BodyContent" +
                           "foo-content" +
                           Environment.NewLine +
                           Environment.NewLine;

            var page = new TestableRazorPage(v =>
            {
                v.HtmlEncoder = htmlEncoder;
                v.Layout = "~/Shared/Layout1.cshtml";
                v.WriteLiteral("BodyContent");
                v.DefineSection("foo", async writer =>
                {
                    await writer.WriteLineAsync("foo-content");
                });
            });
            var nestedLayout = new TestableRazorPage(v =>
            {
                v.HtmlEncoder = htmlEncoder;
                v.Layout = "~/Shared/Layout2.cshtml";
                v.Write("NestedLayout" + Environment.NewLine);
                v.RenderBodyPublic();
                v.DefineSection("foo", async writer =>
                {
                    await writer.WriteLineAsync(htmlEncoder.HtmlEncode(v.RenderSection("foo").ToString()));
                });
            });
            var baseLayout = new TestableRazorPage(v =>
            {
                v.HtmlEncoder = htmlEncoder;
                v.Write("BaseLayout" + Environment.NewLine);
                v.RenderBodyPublic();
                v.Write(v.RenderSection("foo"));
            });
            var viewEngine = new Mock<IRazorViewEngine>();
            viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout1.cshtml"))
                       .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout));
            viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout2.cshtml"))
                       .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout));

            var view = new RazorView(viewEngine.Object,
                                     Mock.Of<IRazorPageActivator>(),
                                     CreateViewStartProvider(),
                                     page,
                                     isPartial: false);
            var viewContext = CreateViewContext(view);

            // Act
            await view.RenderAsync(viewContext);

            // Assert
            Assert.Equal(expected, viewContext.Writer.ToString());
        }
        public void HtmlEncode_NullInput_ReturnsNull()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder();

            // Act & assert
            Assert.Null(encoder.HtmlEncode(null));
        }
        public void HtmlEncode_StringSubstring()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder();
            var output = new StringWriter();

            // Act
            encoder.HtmlEncode("Hello+world!", 3, 5, output);

            // Assert
            Assert.Equal("lo&#x2B;wo", output.ToString());
        }
        public void HtmlEncode_EmptyStringInput_ReturnsEmptyString()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder();

            // Act & assert
            Assert.Equal("", encoder.HtmlEncode(""));
        }
        public void HtmlEncode_InputDoesNotRequireEncoding_ReturnsOriginalStringInstance()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder();
            string input = "Hello, there!";

            // Act & assert
            Assert.Same(input, encoder.HtmlEncode(input));
        }
        public void HtmlEncode_BadSurrogates_ReturnsUnicodeReplacementChar()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All); // allow all codepoints

            // "a<unpaired leading>b<unpaired trailing>c<trailing before leading>d<unpaired trailing><valid>e<high at end of string>"
            const string input = "a\uD800b\uDFFFc\uDFFF\uD800d\uDFFF\uD800\uDFFFe\uD800";
            const string expected = "a\uFFFDb\uFFFDc\uFFFD\uFFFDd\uFFFD&#x103FF;e\uFFFD";

            // Act
            string retVal = encoder.HtmlEncode(input);

            // Assert
            Assert.Equal(expected, retVal);
        }
示例#30
0
 public void HtmlEncode_NullInput_Throws()
 {
     // Arrange
     HtmlEncoder encoder = new HtmlEncoder();
     Assert.Throws<ArgumentNullException>(() => { encoder.HtmlEncode(null); });
 }
        public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All);

            // Act & assert - BMP chars
            for (int i = 0; i <= 0xFFFF; i++)
            {
                string input = new String((char)i, 1);
                string expected;
                if (IsSurrogateCodePoint(i))
                {
                    expected = "\uFFFD"; // unpaired surrogate -> Unicode replacement char
                }
                else
                {
                    if (input == "<") { expected = "&lt;"; }
                    else if (input == ">") { expected = "&gt;"; }
                    else if (input == "&") { expected = "&amp;"; }
                    else if (input == "\"") { expected = "&quot;"; }
                    else
                    {
                        bool mustEncode = false;
                        if (i == '\'' || i == '+')
                        {
                            mustEncode = true; // apostrophe, plus
                        }
                        else if (i <= 0x001F || (0x007F <= i && i <= 0x9F))
                        {
                            mustEncode = true; // control char
                        }
                        else if (!UnicodeHelpers.IsCharacterDefined((char)i))
                        {
                            mustEncode = true; // undefined (or otherwise disallowed) char
                        }

                        if (mustEncode)
                        {
                            expected = String.Format(CultureInfo.InvariantCulture, "&#x{0:X};", i);
                        }
                        else
                        {
                            expected = input; // no encoding
                        }
                    }
                }

                string retVal = encoder.HtmlEncode(input);
                Assert.Equal(expected, retVal);
            }

            // Act & assert - astral chars
            for (int i = 0x10000; i <= 0x10FFFF; i++)
            {
                string input = Char.ConvertFromUtf32(i);
                string expected = String.Format(CultureInfo.InvariantCulture, "&#x{0:X};", i);
                string retVal = encoder.HtmlEncode(input);
                Assert.Equal(expected, retVal);
            }
        }
示例#32
0
        public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended()
        {
            // Arrange
            HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All);

            // Act & assert - BMP chars
            for (int i = 0; i <= 0xFFFF; i++)
            {
                string input = new string((char)i, 1);
                string expected;
                if (IsSurrogateCodePoint(i))
                {
                    expected = "\uFFFD"; // unpaired surrogate -> Unicode replacement char
                }
                else
                {
                    if (input == "<")
                    {
                        expected = "&lt;";
                    }
                    else if (input == ">")
                    {
                        expected = "&gt;";
                    }
                    else if (input == "&")
                    {
                        expected = "&amp;";
                    }
                    else if (input == "\"")
                    {
                        expected = "&quot;";
                    }
                    else
                    {
                        bool mustEncode = false;
                        if (i == '\'' || i == '+')
                        {
                            mustEncode = true; // apostrophe, plus
                        }
                        else if (i <= 0x001F || (0x007F <= i && i <= 0x9F))
                        {
                            mustEncode = true; // control char
                        }
                        else if (!UnicodeHelpers.IsCharacterDefined((char)i))
                        {
                            mustEncode = true; // undefined (or otherwise disallowed) char
                        }

                        if (mustEncode)
                        {
                            expected = string.Format(CultureInfo.InvariantCulture, "&#x{0:X};", i);
                        }
                        else
                        {
                            expected = input; // no encoding
                        }
                    }
                }

                string retVal = encoder.HtmlEncode(input);
                Assert.Equal(expected, retVal);
            }

            // Act & assert - astral chars
            for (int i = 0x10000; i <= 0x10FFFF; i++)
            {
                string input    = char.ConvertFromUtf32(i);
                string expected = string.Format(CultureInfo.InvariantCulture, "&#x{0:X};", i);
                string retVal   = encoder.HtmlEncode(input);
                Assert.Equal(expected, retVal);
            }
        }
示例#33
0
        public async Task RenderAsync_ExecutesLayoutPages()
        {
            // Arrange
            var htmlEncoder = new HtmlEncoder();
            var htmlEncodedNewLine = htmlEncoder.HtmlEncode(Environment.NewLine);
            var expected = "layout-content" +
                           htmlEncodedNewLine +
                           "head-content" +
                           htmlEncodedNewLine +
                           "body-content" +
                           htmlEncodedNewLine +
                           "foot-content";

            var page = new TestableRazorPage(v =>
            {
                v.HtmlEncoder = htmlEncoder;
                v.WriteLiteral("body-content");
                v.Layout = LayoutPath;
                v.DefineSection("head", async writer =>
                {
                    await writer.WriteAsync("head-content");
                });
                v.DefineSection("foot", async writer =>
                {
                    await writer.WriteAsync("foot-content");
                });
            });
            var layout = new TestableRazorPage(v =>
            {
                v.HtmlEncoder = htmlEncoder;
                v.Write("layout-content" + Environment.NewLine);
                v.Write(v.RenderSection("head"));
                v.Write(Environment.NewLine);
                v.RenderBodyPublic();
                v.Write(Environment.NewLine);
                v.Write(v.RenderSection("foot"));
            });
            var activator = new Mock<IRazorPageActivator>();
            activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
                     .Verifiable();
            activator.Setup(a => a.Activate(layout, It.IsAny<ViewContext>()))
                     .Verifiable();
            var viewEngine = new Mock<IRazorViewEngine>();

            var view = new RazorView(viewEngine.Object,
                                     activator.Object,
                                     CreateViewStartProvider(),
                                     page,
                                     isPartial: false);
            var viewContext = CreateViewContext(view);
            viewEngine.Setup(p => p.FindPage(viewContext, LayoutPath))
                       .Returns(new RazorPageResult(LayoutPath, layout))
                       .Verifiable();

            // Act
            await view.RenderAsync(viewContext);

            // Assert
            // Verify the activator was invoked for the primary page and layout page.
            activator.Verify();
            Assert.Equal(expected, viewContext.Writer.ToString());
            viewEngine.Verify();
        }