parent
a2023d35ee
commit
5d32d224f4
|
|
@ -6,8 +6,24 @@ using Microsoft.AspNet.Razor;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the contracts for a Razor host that parses Razor files and generates C# code.
|
||||||
|
/// </summary>
|
||||||
public interface IMvcRazorHost
|
public interface IMvcRazorHost
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Flag that indicates if page execution instrumentation code should be injected into the output.
|
||||||
|
/// </summary>
|
||||||
|
bool EnableInstrumentation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses and generates the contents of a Razor file represented by <paramref name="inputStream"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rootRelativePath">The path of the relative to the root of the application.
|
||||||
|
/// Used to generate line pragmas and calculate the class name of the generated type.</param>
|
||||||
|
/// <param name="inputStream">A <see cref="Stream"/> that represents the Razor contents.</param>
|
||||||
|
/// <returns>A <see cref="GeneratorResults"/> instance that represents the results of code generation.
|
||||||
|
/// </returns>
|
||||||
GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream);
|
GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
templateTypeName: "Microsoft.AspNet.Mvc.Razor.HelperResult",
|
templateTypeName: "Microsoft.AspNet.Mvc.Razor.HelperResult",
|
||||||
defineSectionMethodName: "DefineSection")
|
defineSectionMethodName: "DefineSection")
|
||||||
{
|
{
|
||||||
ResolveUrlMethodName = "Href"
|
ResolveUrlMethodName = "Href",
|
||||||
|
BeginContextMethodName = "BeginContext",
|
||||||
|
EndContextMethodName = "EndContext"
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var ns in _defaultNamespaces)
|
foreach (var ns in _defaultNamespaces)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
|
|
@ -44,6 +45,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Layout { get; set; }
|
string Layout { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a <see cref="IPageExecutionContext"/> instance used to instrument the page execution.
|
||||||
|
/// </summary>
|
||||||
|
IPageExecutionContext PageExecutionContext { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the sections that can be rendered by this page.
|
/// Gets or sets the sections that can be rendered by this page.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,22 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("FlushPointCannotBeInvoked"), p0);
|
return string.Format(CultureInfo.CurrentCulture, GetString("FlushPointCannotBeInvoked"), p0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The {0} returned by '{1}' must be an instance of '{2}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Instrumentation_WriterMustBeBufferedTextWriter
|
||||||
|
{
|
||||||
|
get { return GetString("Instrumentation_WriterMustBeBufferedTextWriter"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The {0} returned by '{1}' must be an instance of '{2}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInstrumentation_WriterMustBeBufferedTextWriter(object p0, object p1, object p2)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("Instrumentation_WriterMustBeBufferedTextWriter"), p0, p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The layout view '{0}' could not be located.
|
/// The layout view '{0}' could not be located.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Mvc.Rendering;
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
@ -47,8 +48,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ViewContext ViewContext { get; set; }
|
public ViewContext ViewContext { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string Layout { get; set; }
|
public string Layout { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IPageExecutionContext PageExecutionContext { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the TextWriter that the page is writing output to.
|
/// Gets the TextWriter that the page is writing output to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -268,6 +273,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
// Calculate length of the source span by the position of the next value (or suffix)
|
// Calculate length of the source span by the position of the next value (or suffix)
|
||||||
var sourceLength = next.Position - attrVal.Value.Position;
|
var sourceLength = next.Position - attrVal.Value.Position;
|
||||||
|
|
||||||
|
BeginContext(attrVal.Value.Position, sourceLength, isLiteral: attrVal.Literal);
|
||||||
// The extra branching here is to ensure that we call the Write*To(string) overload whe
|
// The extra branching here is to ensure that we call the Write*To(string) overload whe
|
||||||
// possible.
|
// possible.
|
||||||
if (attrVal.Literal && stringValue != null)
|
if (attrVal.Literal && stringValue != null)
|
||||||
|
|
@ -287,6 +293,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
WriteTo(writer, val.Value);
|
WriteTo(writer, val.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EndContext();
|
||||||
wroteSomething = true;
|
wroteSomething = true;
|
||||||
}
|
}
|
||||||
if (wroteSomething)
|
if (wroteSomething)
|
||||||
|
|
@ -308,7 +315,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
private void WritePositionTaggedLiteral(TextWriter writer, string value, int position)
|
private void WritePositionTaggedLiteral(TextWriter writer, string value, int position)
|
||||||
{
|
{
|
||||||
|
BeginContext(position, value.Length, isLiteral: true);
|
||||||
WriteLiteralTo(writer, value);
|
WriteLiteralTo(writer, value);
|
||||||
|
EndContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WritePositionTaggedLiteral(TextWriter writer, PositionTagged<string> value)
|
private void WritePositionTaggedLiteral(TextWriter writer, PositionTagged<string> value)
|
||||||
|
|
@ -420,6 +429,16 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void BeginContext(int position, int length, bool isLiteral)
|
||||||
|
{
|
||||||
|
PageExecutionContext?.BeginContext(position, length, isLiteral);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndContext()
|
||||||
|
{
|
||||||
|
PageExecutionContext?.EndContext();
|
||||||
|
}
|
||||||
|
|
||||||
private void EnsureMethodCanBeInvoked(string methodName)
|
private void EnsureMethodCanBeInvoked(string methodName)
|
||||||
{
|
{
|
||||||
if (PreviousSectionWriters == null)
|
if (PreviousSectionWriters == null)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// This is primarily designed to avoid creating large in-memory strings.
|
/// This is primarily designed to avoid creating large in-memory strings.
|
||||||
/// Refer to https://aspnetwebstack.codeplex.com/workitem/585 for more details.
|
/// Refer to https://aspnetwebstack.codeplex.com/workitem/585 for more details.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class RazorTextWriter : TextWriter
|
public class RazorTextWriter : TextWriter, IBufferedTextWriter
|
||||||
{
|
{
|
||||||
private static readonly Task _completedTask = Task.FromResult(0);
|
private static readonly Task _completedTask = Task.FromResult(0);
|
||||||
private readonly TextWriter _unbufferedWriter;
|
private readonly TextWriter _unbufferedWriter;
|
||||||
|
|
@ -43,9 +43,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
get { return _encoding; }
|
get { return _encoding; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Gets a flag that determines if this instance of <see cref="RazorTextWriter"/> is buffering content.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsBuffering { get; private set; } = true;
|
public bool IsBuffering { get; private set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -267,10 +265,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
await _unbufferedWriter.FlushAsync();
|
await _unbufferedWriter.FlushAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Copies the content of the <see cref="RazorTextWriter"/> to the <see cref="TextWriter"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer">The writer to copy contents to.</param>
|
|
||||||
public void CopyTo(TextWriter writer)
|
public void CopyTo(TextWriter writer)
|
||||||
{
|
{
|
||||||
var targetRazorTextWriter = writer as RazorTextWriter;
|
var targetRazorTextWriter = writer as RazorTextWriter;
|
||||||
|
|
@ -286,11 +281,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Copies the content of the <see cref="RazorTextWriter"/> to the specified <see cref="TextWriter"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer">The writer to copy contents to.</param>
|
|
||||||
/// <returns>A task that represents the asynchronous copy operation.</returns>
|
|
||||||
public Task CopyToAsync(TextWriter writer)
|
public Task CopyToAsync(TextWriter writer)
|
||||||
{
|
{
|
||||||
var targetRazorTextWriter = writer as RazorTextWriter;
|
var targetRazorTextWriter = writer as RazorTextWriter;
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,9 @@
|
||||||
<data name="FlushPointCannotBeInvoked" xml:space="preserve">
|
<data name="FlushPointCannotBeInvoked" xml:space="preserve">
|
||||||
<value>'{0}' cannot be invoked when a Layout page is set to be executed.</value>
|
<value>'{0}' cannot be invoked when a Layout page is set to be executed.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Instrumentation_WriterMustBeBufferedTextWriter" xml:space="preserve">
|
||||||
|
<value>The {0} returned by '{1}' must be an instance of '{2}'.</value>
|
||||||
|
</data>
|
||||||
<data name="LayoutCannotBeLocated" xml:space="preserve">
|
<data name="LayoutCannotBeLocated" xml:space="preserve">
|
||||||
<value>The layout view '{0}' could not be located.</value>
|
<value>The layout view '{0}' could not be located.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Framework.Runtime;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the contracts for a <see cref="TextWriter"/> that buffers its content.
|
||||||
|
/// </summary>
|
||||||
|
[AssemblyNeutral]
|
||||||
|
public interface IBufferedTextWriter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a flag that determines if content is currently being buffered.
|
||||||
|
/// </summary>
|
||||||
|
bool IsBuffering { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies the buffered content to the <paramref name="writer"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer">The writer to copy the contents to./param>
|
||||||
|
void CopyTo(TextWriter writer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously copies the buffered content to the <paramref name="writer"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer">The writer to copy the contents to./param>
|
||||||
|
/// <returns>A <see cref="Task"/> representing the copy operation.</returns>
|
||||||
|
Task CopyToAsync(TextWriter writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.Framework.Runtime;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.PageExecutionInstrumentation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the contracts for a execution context that instruments web page execution.
|
||||||
|
/// </summary>
|
||||||
|
[AssemblyNeutral]
|
||||||
|
public interface IPageExecutionContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked at the start of a write operation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="position">The absolute character position of the expression or text in the Razor file.</param>
|
||||||
|
/// <param name="length">The character length of the expression or text in the Razor file.</param>
|
||||||
|
/// <param name="isLiteral">A flag that indicates if the operation is for a literal text and not for a
|
||||||
|
/// language expression.</param>
|
||||||
|
void BeginContext(int position, int length, bool isLiteral);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked at the end of a write operation.
|
||||||
|
/// </summary>
|
||||||
|
void EndContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.Framework.Runtime;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.PageExecutionInstrumentation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the contracts for a HTTP feature that provides the context to instrument a web page.
|
||||||
|
/// </summary>
|
||||||
|
[AssemblyNeutral]
|
||||||
|
public interface IPageExecutionListenerFeature
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Decorates the <see cref="TextWriter"/> used by web page instances to
|
||||||
|
/// write the result to.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer">The output <see cref="TextWriter"/> for the web page.</param>
|
||||||
|
/// <returns>A <see cref="TextWriter"/> that wraps <paramref name="writer"/>.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
TextWriter DecorateWriter(TextWriter writer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="IPageExecutionContext"/> for the specified <paramref name="sourceFilePath"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourceFilePath">The path of the <see cref="Mvc.Razor.IRazorPage"/>.</param>
|
||||||
|
/// <param name="writer">The <see cref="TextWriter"/> obtained from <see cref="DecorateWriter(TextWriter)"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <returns></returns>
|
||||||
|
IPageExecutionContext GetContext(string sourceFilePath, TextWriter writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,14 +2,10 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using Microsoft.AspNet.Razor;
|
|
||||||
using Microsoft.AspNet.Razor.Generator;
|
using Microsoft.AspNet.Razor.Generator;
|
||||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||||
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
|
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
|
||||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||||
using Microsoft.AspNet.Razor.Text;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.AspNet.Mvc.Rendering;
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||||
using Microsoft.AspNet.PipelineCore;
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -351,6 +351,68 @@ Layout end
|
||||||
Assert.DoesNotThrow(() => page.SectionWriters["test-section"].WriteTo(TextWriter.Null));
|
Assert.DoesNotThrow(() => page.SectionWriters["test-section"].WriteTo(TextWriter.Null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WriteAttribute_CallsBeginAndEndContext_OnPageExecutionListenerContext()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var page = CreatePage(p =>
|
||||||
|
{
|
||||||
|
p.WriteAttribute("href",
|
||||||
|
new PositionTagged<string>("prefix", 0),
|
||||||
|
new PositionTagged<string>("suffix", 34),
|
||||||
|
new AttributeValue(new PositionTagged<string>("prefix", 0),
|
||||||
|
new PositionTagged<object>("attr1-value", 8),
|
||||||
|
literal: true),
|
||||||
|
new AttributeValue(new PositionTagged<string>("prefix2", 22),
|
||||||
|
new PositionTagged<object>("attr2", 29),
|
||||||
|
literal: false));
|
||||||
|
});
|
||||||
|
var context = new Mock<IPageExecutionContext>(MockBehavior.Strict);
|
||||||
|
var sequence = new MockSequence();
|
||||||
|
context.InSequence(sequence).Setup(f => f.BeginContext(0, 6, true)).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.BeginContext(8, 14, true)).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.BeginContext(22, 7, true)).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.BeginContext(29, 5, false)).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.BeginContext(34, 6, true)).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||||
|
page.PageExecutionContext = context.Object;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await page.ExecuteAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
context.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WriteAttribute_CallsBeginAndEndContext_OnPrefixAndSuffixValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var page = CreatePage(p =>
|
||||||
|
{
|
||||||
|
p.WriteAttribute("href",
|
||||||
|
new PositionTagged<string>("prefix", 0),
|
||||||
|
new PositionTagged<string>("tail", 7));
|
||||||
|
});
|
||||||
|
var context = new Mock<IPageExecutionContext>(MockBehavior.Strict);
|
||||||
|
var sequence = new MockSequence();
|
||||||
|
context.InSequence(sequence).Setup(f => f.BeginContext(0, 6, true)).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.BeginContext(7, 4, true)).Verifiable();
|
||||||
|
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||||
|
page.PageExecutionContext = context.Object;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await page.ExecuteAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
context.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
private static TestableRazorPage CreatePage(Action<TestableRazorPage> executeAction,
|
private static TestableRazorPage CreatePage(Action<TestableRazorPage> executeAction,
|
||||||
ViewContext context = null)
|
ViewContext context = null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue