Shorten Microsoft.AspNetCore.Razor.Language.Test paths (dotnet/aspnetcore-tooling#425)
* Shorten Microsoft.AspNetCore.Razor.Language.Test paths
- aspnet/AspNetCoredotnet/aspnetcore-tooling#9358 and aspnet/AspNetCoredotnet/aspnetcore-tooling#7882
- do 4046 renames
- adjust solutions to renamed directory
- special-case directory of the Microsoft.AspNetCore.Razor.Language.Test project in `TestProject`\n\nCommit migrated from 72b939d855
This commit is contained in:
parent
90eb9e8535
commit
ae4af3d154
|
|
@ -0,0 +1,139 @@
|
|||
// 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 Microsoft.AspNetCore.Components.RenderTree;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components
|
||||
{
|
||||
internal static class AssertFrame
|
||||
{
|
||||
public static void Sequence(RenderTreeFrame frame, int? sequence = null)
|
||||
{
|
||||
if (sequence.HasValue)
|
||||
{
|
||||
Assert.Equal(sequence.Value, frame.Sequence);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Text(RenderTreeFrame frame, string textContent, int? sequence = null)
|
||||
{
|
||||
Assert.Equal(RenderTreeFrameType.Text, frame.FrameType);
|
||||
Assert.Equal(textContent, frame.TextContent);
|
||||
Assert.Equal(0, frame.ElementSubtreeLength);
|
||||
AssertFrame.Sequence(frame, sequence);
|
||||
}
|
||||
|
||||
internal static void Markup(RenderTreeFrame frame, string markupContent, int? sequence = null)
|
||||
{
|
||||
Assert.Equal(RenderTreeFrameType.Markup, frame.FrameType);
|
||||
Assert.Equal(markupContent, frame.MarkupContent);
|
||||
Assert.Equal(0, frame.ElementSubtreeLength);
|
||||
AssertFrame.Sequence(frame, sequence);
|
||||
}
|
||||
|
||||
public static void Element(RenderTreeFrame frame, string elementName, int subtreeLength, int? sequence = null)
|
||||
{
|
||||
Assert.Equal(RenderTreeFrameType.Element, frame.FrameType);
|
||||
Assert.Equal(elementName, frame.ElementName);
|
||||
Assert.Equal(subtreeLength, frame.ElementSubtreeLength);
|
||||
AssertFrame.Sequence(frame, sequence);
|
||||
}
|
||||
|
||||
public static void Attribute(RenderTreeFrame frame, string attributeName, int? sequence = null)
|
||||
{
|
||||
Assert.Equal(RenderTreeFrameType.Attribute, frame.FrameType);
|
||||
Assert.Equal(attributeName, frame.AttributeName);
|
||||
AssertFrame.Sequence(frame, sequence);
|
||||
}
|
||||
|
||||
public static void Attribute(RenderTreeFrame frame, string attributeName, string attributeValue, int? sequence = null)
|
||||
{
|
||||
AssertFrame.Attribute(frame, attributeName, sequence);
|
||||
Assert.Equal(attributeValue, frame.AttributeValue);
|
||||
}
|
||||
|
||||
public static void Attribute(RenderTreeFrame frame, string attributeName, Action<UIEventArgs> attributeEventHandlerValue, int? sequence = null)
|
||||
{
|
||||
AssertFrame.Attribute(frame, attributeName, sequence);
|
||||
Assert.Equal(attributeEventHandlerValue, frame.AttributeValue);
|
||||
}
|
||||
|
||||
public static void Attribute(RenderTreeFrame frame, string attributeName, object attributeValue, int? sequence = null)
|
||||
{
|
||||
AssertFrame.Attribute(frame, attributeName, sequence);
|
||||
Assert.Equal(attributeValue, frame.AttributeValue);
|
||||
}
|
||||
|
||||
public static void Attribute(RenderTreeFrame frame, string attributeName, Type valueType, int? sequence = null)
|
||||
{
|
||||
AssertFrame.Attribute(frame, attributeName, sequence);
|
||||
Assert.IsType(valueType, frame.AttributeValue);
|
||||
}
|
||||
|
||||
public static void Attribute(RenderTreeFrame frame, string attributeName, Action<object> attributeValidator, int? sequence = null)
|
||||
{
|
||||
AssertFrame.Attribute(frame, attributeName, sequence);
|
||||
attributeValidator(frame.AttributeValue);
|
||||
}
|
||||
|
||||
public static void Component<T>(RenderTreeFrame frame, int? subtreeLength = null, int? sequence = null) where T : IComponent
|
||||
{
|
||||
Component(frame, typeof(T).FullName, subtreeLength, sequence);
|
||||
}
|
||||
|
||||
public static void Component(RenderTreeFrame frame, string typeName, int? subtreeLength = null, int? sequence = null)
|
||||
{
|
||||
Assert.Equal(RenderTreeFrameType.Component, frame.FrameType);
|
||||
Assert.Equal(typeName, frame.ComponentType.FullName);
|
||||
if (subtreeLength.HasValue)
|
||||
{
|
||||
Assert.Equal(subtreeLength.Value, frame.ComponentSubtreeLength);
|
||||
}
|
||||
AssertFrame.Sequence(frame, sequence);
|
||||
}
|
||||
|
||||
public static void ComponentWithInstance<T>(RenderTreeFrame frame, int componentId, int? subtreeLength = null, int? sequence = null) where T : IComponent
|
||||
{
|
||||
AssertFrame.Component<T>(frame, subtreeLength, sequence);
|
||||
Assert.IsType<T>(frame.Component);
|
||||
Assert.Equal(componentId, frame.ComponentId);
|
||||
}
|
||||
|
||||
public static void Region(RenderTreeFrame frame, int subtreeLength, int? sequence = null)
|
||||
{
|
||||
Assert.Equal(RenderTreeFrameType.Region, frame.FrameType);
|
||||
Assert.Equal(subtreeLength, frame.RegionSubtreeLength);
|
||||
AssertFrame.Sequence(frame, sequence);
|
||||
}
|
||||
|
||||
public static void Whitespace(RenderTreeFrame frame, int? sequence = null)
|
||||
{
|
||||
Assert.Equal(RenderTreeFrameType.Text, frame.FrameType);
|
||||
AssertFrame.Sequence(frame, sequence);
|
||||
Assert.True(string.IsNullOrWhiteSpace(frame.TextContent));
|
||||
}
|
||||
|
||||
public static void ElementReferenceCapture(RenderTreeFrame frame, Action<ElementRef> action, int? sequence = null)
|
||||
{
|
||||
Assert.Equal(RenderTreeFrameType.ElementReferenceCapture, frame.FrameType);
|
||||
Assert.Same(action, frame.ElementReferenceCaptureAction);
|
||||
AssertFrame.Sequence(frame, sequence);
|
||||
}
|
||||
|
||||
public static void ComponentReferenceCapture(RenderTreeFrame frame, int? sequence = null)
|
||||
{
|
||||
Assert.Equal(RenderTreeFrameType.ComponentReferenceCapture, frame.FrameType);
|
||||
Assert.NotNull(frame.ComponentReferenceCaptureAction);
|
||||
AssertFrame.Sequence(frame, sequence);
|
||||
}
|
||||
|
||||
public static void ComponentReferenceCapture(RenderTreeFrame frame, Action<object> action, int? sequence = null)
|
||||
{
|
||||
Assert.Equal(RenderTreeFrameType.ComponentReferenceCapture, frame.FrameType);
|
||||
Assert.Same(action, frame.ComponentReferenceCaptureAction);
|
||||
AssertFrame.Sequence(frame, sequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class BoundAttributeDescriptorExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetPropertyName_ReturnsPropertyName()
|
||||
{
|
||||
// Arrange
|
||||
var expectedPropertyName = "IntProperty";
|
||||
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("test")
|
||||
.PropertyName(expectedPropertyName)
|
||||
.TypeName(typeof(int).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var propertyName = descriptor.GetPropertyName();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedPropertyName, propertyName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPropertyName_ReturnsNullIfNoPropertyName()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("test")
|
||||
.TypeName(typeof(int).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var propertyName = descriptor.GetPropertyName();
|
||||
|
||||
// Assert
|
||||
Assert.Null(propertyName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsDefaultKind_ReturnsTrue_IfKindIsDefault()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("test")
|
||||
.PropertyName("IntProperty")
|
||||
.TypeName(typeof(int).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var isDefault = descriptor.IsDefaultKind();
|
||||
|
||||
// Assert
|
||||
Assert.True(isDefault);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsDefaultKind_ReturnsFalse_IfKindIsNotDefault()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder("other-kind", "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, "other-kind");
|
||||
builder
|
||||
.Name("test")
|
||||
.PropertyName("IntProperty")
|
||||
.TypeName(typeof(int).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var isDefault = descriptor.IsDefaultKind();
|
||||
|
||||
// Assert
|
||||
Assert.False(isDefault);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpectsStringValue_ReturnsTrue_ForStringProperty()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("test")
|
||||
.PropertyName("BoundProp")
|
||||
.TypeName(typeof(string).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var result = descriptor.ExpectsStringValue("test");
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpectsStringValue_ReturnsFalse_ForNonStringProperty()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("test")
|
||||
.PropertyName("BoundProp")
|
||||
.TypeName(typeof(bool).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var result = descriptor.ExpectsStringValue("test");
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpectsStringValue_ReturnsTrue_StringIndexerAndNameMatch()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("test")
|
||||
.PropertyName("BoundProp")
|
||||
.TypeName("System.Collection.Generic.IDictionary<string, string>")
|
||||
.AsDictionary("prefix-test-", typeof(string).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var result = descriptor.ExpectsStringValue("prefix-test-key");
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpectsStringValue_ReturnsFalse_StringIndexerAndNameMismatch()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("test")
|
||||
.PropertyName("BoundProp")
|
||||
.TypeName("System.Collection.Generic.IDictionary<string, string>")
|
||||
.AsDictionary("prefix-test-", typeof(string).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var result = descriptor.ExpectsStringValue("test");
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpectsBooleanValue_ReturnsTrue_ForBooleanProperty()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("test")
|
||||
.PropertyName("BoundProp")
|
||||
.TypeName(typeof(bool).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var result = descriptor.ExpectsBooleanValue("test");
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpectsBooleanValue_ReturnsFalse_ForNonBooleanProperty()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("test")
|
||||
.PropertyName("BoundProp")
|
||||
.TypeName(typeof(int).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var result = descriptor.ExpectsBooleanValue("test");
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpectsBooleanValue_ReturnsTrue_BooleanIndexerAndNameMatch()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("test")
|
||||
.PropertyName("BoundProp")
|
||||
.TypeName("System.Collection.Generic.IDictionary<string, bool>")
|
||||
.AsDictionary("prefix-test-", typeof(bool).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var result = descriptor.ExpectsBooleanValue("prefix-test-key");
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpectsBooleanValue_ReturnsFalse_BooleanIndexerAndNameMismatch()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("test")
|
||||
.PropertyName("BoundProp")
|
||||
.TypeName("System.Collection.Generic.IDictionary<string, bool>")
|
||||
.AsDictionary("prefix-test-", typeof(bool).FullName);
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Act
|
||||
var result = descriptor.ExpectsBooleanValue("test");
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,339 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
{
|
||||
public class CSharpCodeWriterTest
|
||||
{
|
||||
// The length of the newline string written by writer.WriteLine.
|
||||
private static readonly int WriterNewLineLength = Environment.NewLine.Length;
|
||||
|
||||
public static IEnumerable<object[]> NewLines
|
||||
{
|
||||
get
|
||||
{
|
||||
return new object[][]
|
||||
{
|
||||
new object[] { "\r" },
|
||||
new object[] { "\n" },
|
||||
new object[] { "\r\n" },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCodeWriter_TracksPosition_WithWrite()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.Write("1234");
|
||||
|
||||
// Assert
|
||||
var location = writer.Location;
|
||||
var expected = new SourceLocation(absoluteIndex: 4, lineIndex: 0, characterIndex: 4);
|
||||
|
||||
Assert.Equal(expected, location);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCodeWriter_TracksPosition_WithIndent()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.WriteLine();
|
||||
writer.Indent(size: 3);
|
||||
|
||||
// Assert
|
||||
var location = writer.Location;
|
||||
var expected = new SourceLocation(absoluteIndex: 3 + WriterNewLineLength, lineIndex: 1, characterIndex: 3);
|
||||
|
||||
Assert.Equal(expected, location);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCodeWriter_TracksPosition_WithWriteLine()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.WriteLine("1234");
|
||||
|
||||
// Assert
|
||||
var location = writer.Location;
|
||||
|
||||
var expected = new SourceLocation(absoluteIndex: 4 + WriterNewLineLength, lineIndex: 1, characterIndex: 0);
|
||||
|
||||
Assert.Equal(expected, location);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(NewLines))]
|
||||
public void CSharpCodeWriter_TracksPosition_WithWriteLine_WithNewLineInContent(string newLine)
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.WriteLine("1234" + newLine + "12");
|
||||
|
||||
// Assert
|
||||
var location = writer.Location;
|
||||
|
||||
var expected = new SourceLocation(
|
||||
absoluteIndex: 6 + newLine.Length + WriterNewLineLength,
|
||||
lineIndex: 2,
|
||||
characterIndex: 0);
|
||||
|
||||
Assert.Equal(expected, location);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(NewLines))]
|
||||
public void CSharpCodeWriter_TracksPosition_WithWrite_WithNewlineInContent(string newLine)
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.Write("1234" + newLine + "123" + newLine + "12");
|
||||
|
||||
// Assert
|
||||
var location = writer.Location;
|
||||
|
||||
var expected = new SourceLocation(
|
||||
absoluteIndex: 9 + newLine.Length + newLine.Length,
|
||||
lineIndex: 2,
|
||||
characterIndex: 2);
|
||||
|
||||
Assert.Equal(expected, location);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCodeWriter_TracksPosition_WithWrite_WithNewlineInContent_RepeatedN()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.Write("1234\n\n123");
|
||||
|
||||
// Assert
|
||||
var location = writer.Location;
|
||||
|
||||
var expected = new SourceLocation(
|
||||
absoluteIndex: 9,
|
||||
lineIndex: 2,
|
||||
characterIndex: 3);
|
||||
|
||||
Assert.Equal(expected, location);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCodeWriter_TracksPosition_WithWrite_WithMixedNewlineInContent()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.Write("1234\r123\r\n12\n1");
|
||||
|
||||
// Assert
|
||||
var location = writer.Location;
|
||||
|
||||
var expected = new SourceLocation(
|
||||
absoluteIndex: 14,
|
||||
lineIndex: 3,
|
||||
characterIndex: 1);
|
||||
|
||||
Assert.Equal(expected, location);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCodeWriter_TracksPosition_WithNewline_SplitAcrossWrites()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.Write("1234\r");
|
||||
var location1 = writer.Location;
|
||||
|
||||
writer.Write("\n");
|
||||
var location2 = writer.Location;
|
||||
|
||||
// Assert
|
||||
var expected1 = new SourceLocation(absoluteIndex: 5, lineIndex: 1, characterIndex: 0);
|
||||
Assert.Equal(expected1, location1);
|
||||
|
||||
var expected2 = new SourceLocation(absoluteIndex: 6, lineIndex: 1, characterIndex: 0);
|
||||
Assert.Equal(expected2, location2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCodeWriter_TracksPosition_WithTwoNewline_SplitAcrossWrites_R()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.Write("1234\r");
|
||||
var location1 = writer.Location;
|
||||
|
||||
writer.Write("\r");
|
||||
var location2 = writer.Location;
|
||||
|
||||
// Assert
|
||||
var expected1 = new SourceLocation(absoluteIndex: 5, lineIndex: 1, characterIndex: 0);
|
||||
Assert.Equal(expected1, location1);
|
||||
|
||||
var expected2 = new SourceLocation(absoluteIndex: 6, lineIndex: 2, characterIndex: 0);
|
||||
Assert.Equal(expected2, location2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCodeWriter_TracksPosition_WithTwoNewline_SplitAcrossWrites_N()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.Write("1234\n");
|
||||
var location1 = writer.Location;
|
||||
|
||||
writer.Write("\n");
|
||||
var location2 = writer.Location;
|
||||
|
||||
// Assert
|
||||
var expected1 = new SourceLocation(absoluteIndex: 5, lineIndex: 1, characterIndex: 0);
|
||||
Assert.Equal(expected1, location1);
|
||||
|
||||
var expected2 = new SourceLocation(absoluteIndex: 6, lineIndex: 2, characterIndex: 0);
|
||||
Assert.Equal(expected2, location2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCodeWriter_TracksPosition_WithTwoNewline_SplitAcrossWrites_Reversed()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.Write("1234\n");
|
||||
var location1 = writer.Location;
|
||||
|
||||
writer.Write("\r");
|
||||
var location2 = writer.Location;
|
||||
|
||||
// Assert
|
||||
var expected1 = new SourceLocation(absoluteIndex: 5, lineIndex: 1, characterIndex: 0);
|
||||
Assert.Equal(expected1, location1);
|
||||
|
||||
var expected2 = new SourceLocation(absoluteIndex: 6, lineIndex: 2, characterIndex: 0);
|
||||
Assert.Equal(expected2, location2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCodeWriter_TracksPosition_WithNewline_SplitAcrossWrites_AtBeginning()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.Write("\r");
|
||||
var location1 = writer.Location;
|
||||
|
||||
writer.Write("\n");
|
||||
var location2 = writer.Location;
|
||||
|
||||
// Assert
|
||||
var expected1 = new SourceLocation(absoluteIndex: 1, lineIndex: 1, characterIndex: 0);
|
||||
Assert.Equal(expected1, location1);
|
||||
|
||||
var expected2 = new SourceLocation(absoluteIndex: 2, lineIndex: 1, characterIndex: 0);
|
||||
Assert.Equal(expected2, location2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteLineNumberDirective_UsesFilePath_FromSourceLocation()
|
||||
{
|
||||
// Arrange
|
||||
var filePath = "some-path";
|
||||
var mappingLocation = new SourceSpan(filePath, 10, 4, 3, 9);
|
||||
|
||||
var writer = new CodeWriter();
|
||||
var expected = $"#line 5 \"{filePath}\"" + writer.NewLine;
|
||||
|
||||
// Act
|
||||
writer.WriteLineNumberDirective(mappingLocation);
|
||||
var code = writer.GenerateCode();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteField_WritesFieldDeclaration()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.WriteField(new[] { "private" }, "global::System.String", "_myString");
|
||||
|
||||
// Assert
|
||||
var output = writer.GenerateCode();
|
||||
Assert.Equal("private global::System.String _myString;" + Environment.NewLine, output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteField_WithModifiers_WritesFieldDeclaration()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.WriteField(new[] { "private", "readonly", "static" }, "global::System.String", "_myString");
|
||||
|
||||
// Assert
|
||||
var output = writer.GenerateCode();
|
||||
Assert.Equal("private readonly static global::System.String _myString;" + Environment.NewLine, output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteAutoPropertyDeclaration_WritesPropertyDeclaration()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.WriteAutoPropertyDeclaration(new[] { "public" }, "global::System.String", "MyString");
|
||||
|
||||
// Assert
|
||||
var output = writer.GenerateCode();
|
||||
Assert.Equal("public global::System.String MyString { get; set; }" + Environment.NewLine, output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteAutoPropertyDeclaration_WithModifiers_WritesPropertyDeclaration()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CodeWriter();
|
||||
|
||||
// Act
|
||||
writer.WriteAutoPropertyDeclaration(new[] { "public", "static" }, "global::System.String", "MyString");
|
||||
|
||||
// Assert
|
||||
var output = writer.GenerateCode();
|
||||
Assert.Equal("public static global::System.String MyString { get; set; }" + Environment.NewLine, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
{
|
||||
public class CodeTargetTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateDefault_CreatesDefaultCodeTarget()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
// Act
|
||||
var target = CodeTarget.CreateDefault(codeDocument, options);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<DefaultCodeTarget>(target);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDefault_CallsDelegate()
|
||||
{
|
||||
// Arrange
|
||||
var wasCalled = false;
|
||||
Action<CodeTargetBuilder> @delegate = (b) => { wasCalled = true; };
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
// Act
|
||||
CodeTarget.CreateDefault(codeDocument, options, @delegate);
|
||||
|
||||
// Assert
|
||||
Assert.True(wasCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDefault_AllowsNullDelegate()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
// Act
|
||||
CodeTarget.CreateDefault(codeDocument, options, configure: null);
|
||||
|
||||
// Assert (does not throw)
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateEmpty_AllowsNullDelegate()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
// Act
|
||||
CodeTarget.CreateDefault(codeDocument, options, configure: null);
|
||||
|
||||
// Assert (does not throw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
{
|
||||
public class DefaultCodeTargetBuilderTest
|
||||
{
|
||||
[Fact]
|
||||
public void Build_CreatesDefaultCodeTarget()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var builder = new DefaultCodeTargetBuilder(codeDocument, options);
|
||||
|
||||
var extensions = new ICodeTargetExtension[]
|
||||
{
|
||||
new MyExtension1(),
|
||||
new MyExtension2(),
|
||||
};
|
||||
|
||||
for (var i = 0; i < extensions.Length; i++)
|
||||
{
|
||||
builder.TargetExtensions.Add(extensions[i]);
|
||||
}
|
||||
|
||||
// Act
|
||||
var result = builder.Build();
|
||||
|
||||
// Assert
|
||||
var target = Assert.IsType<DefaultCodeTarget>(result);
|
||||
Assert.Equal(extensions, target.Extensions);
|
||||
}
|
||||
|
||||
private class MyExtension1 : ICodeTargetExtension
|
||||
{
|
||||
}
|
||||
|
||||
private class MyExtension2 : ICodeTargetExtension
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
// 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.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
{
|
||||
public class DefaultCodeTargetTest
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_CreatesDefensiveCopy()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var extensions = new ICodeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension1(),
|
||||
};
|
||||
|
||||
// Act
|
||||
var target = new DefaultCodeTarget(options, extensions);
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(extensions, target);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateWriter_DesignTime_CreatesDesignTimeNodeWriter()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorCodeGenerationOptions.CreateDesignTimeDefault();
|
||||
var target = new DefaultCodeTarget(options, Enumerable.Empty<ICodeTargetExtension>());
|
||||
|
||||
// Act
|
||||
var writer = target.CreateNodeWriter();
|
||||
|
||||
// Assert
|
||||
Assert.IsType<DesignTimeNodeWriter>(writer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateWriter_Runtime_CreatesRuntimeNodeWriter()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
var target = new DefaultCodeTarget(options, Enumerable.Empty<ICodeTargetExtension>());
|
||||
|
||||
// Act
|
||||
var writer = target.CreateNodeWriter();
|
||||
|
||||
// Assert
|
||||
Assert.IsType<RuntimeNodeWriter>(writer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasExtension_ReturnsTrue_WhenExtensionFound()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var extensions = new ICodeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension1(),
|
||||
};
|
||||
|
||||
var target = new DefaultCodeTarget(options, extensions);
|
||||
|
||||
// Act
|
||||
var result = target.HasExtension<MyExtension1>();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasExtension_ReturnsFalse_WhenExtensionNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var extensions = new ICodeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension2(),
|
||||
};
|
||||
|
||||
var target = new DefaultCodeTarget(options, extensions);
|
||||
|
||||
// Act
|
||||
var result = target.HasExtension<MyExtension1>();
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExtension_ReturnsExtension_WhenExtensionFound()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var extensions = new ICodeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension1(),
|
||||
};
|
||||
|
||||
var target = new DefaultCodeTarget(options, extensions);
|
||||
|
||||
// Act
|
||||
var result = target.GetExtension<MyExtension1>();
|
||||
|
||||
// Assert
|
||||
Assert.Same(extensions[1], result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExtension_ReturnsFirstMatch_WhenExtensionFound()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var extensions = new ICodeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension1(),
|
||||
new MyExtension2(),
|
||||
new MyExtension1(),
|
||||
};
|
||||
|
||||
var target = new DefaultCodeTarget(options, extensions);
|
||||
|
||||
// Act
|
||||
var result = target.GetExtension<MyExtension1>();
|
||||
|
||||
// Assert
|
||||
Assert.Same(extensions[1], result);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void GetExtension_ReturnsNull_WhenExtensionNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var extensions = new ICodeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension2(),
|
||||
};
|
||||
|
||||
var target = new DefaultCodeTarget(options, extensions);
|
||||
|
||||
// Act
|
||||
var result = target.GetExtension<MyExtension1>();
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
private class MyExtension1 : ICodeTargetExtension
|
||||
{
|
||||
}
|
||||
|
||||
private class MyExtension2 : ICodeTargetExtension
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,393 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
{
|
||||
public class DefaultDocumentWriterTest
|
||||
{
|
||||
[Fact] // This test covers the whole process including actual hashing.
|
||||
public void WriteDocument_EndToEnd_WritesChecksumAndMarksAutoGenerated()
|
||||
{
|
||||
// Arrange
|
||||
var document = new DocumentIntermediateNode();
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var target = CodeTarget.CreateDefault(codeDocument, options);
|
||||
var writer = new DefaultDocumentWriter(target, options);
|
||||
|
||||
// Act
|
||||
var result = writer.WriteDocument(codeDocument, document);
|
||||
|
||||
// Assert
|
||||
var csharp = result.GeneratedCode;
|
||||
Assert.Equal(
|
||||
@"#pragma checksum ""test.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""da39a3ee5e6b4b0d3255bfef95601890afd80709""
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
#pragma warning restore 1591
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDocument_SHA1_WritesChecksumAndMarksAutoGenerated()
|
||||
{
|
||||
// Arrange
|
||||
var checksumBytes = new byte[] { (byte)'t', (byte)'e', (byte)'s', (byte)'t', };
|
||||
|
||||
var sourceDocument = Mock.Of<RazorSourceDocument>(d =>
|
||||
d.FilePath == "test.cshtml" &&
|
||||
d.GetChecksum() == checksumBytes &&
|
||||
d.GetChecksumAlgorithm() == "SHA1");
|
||||
|
||||
var document = new DocumentIntermediateNode();
|
||||
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var target = CodeTarget.CreateDefault(codeDocument, options);
|
||||
var writer = new DefaultDocumentWriter(target, options);
|
||||
|
||||
// Act
|
||||
var result = writer.WriteDocument(codeDocument, document);
|
||||
|
||||
// Assert
|
||||
var csharp = result.GeneratedCode;
|
||||
Assert.Equal(
|
||||
@"#pragma checksum ""test.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""74657374""
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
#pragma warning restore 1591
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDocument_SHA256_WritesChecksumAndMarksAutoGenerated()
|
||||
{
|
||||
// Arrange
|
||||
var checksumBytes = new byte[] { (byte)'t', (byte)'e', (byte)'s', (byte)'t', };
|
||||
|
||||
var sourceDocument = Mock.Of<RazorSourceDocument>(d =>
|
||||
d.FilePath == "test.cshtml" &&
|
||||
d.GetChecksum() == checksumBytes &&
|
||||
d.GetChecksumAlgorithm() == "SHA256");
|
||||
|
||||
var document = new DocumentIntermediateNode();
|
||||
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var target = CodeTarget.CreateDefault(codeDocument, options);
|
||||
var writer = new DefaultDocumentWriter(target, options);
|
||||
|
||||
// Act
|
||||
var result = writer.WriteDocument(codeDocument, document);
|
||||
|
||||
// Assert
|
||||
var csharp = result.GeneratedCode;
|
||||
Assert.Equal(
|
||||
@"#pragma checksum ""test.cshtml"" ""{8829d00f-11b8-4213-878b-770e8597ac16}"" ""74657374""
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
#pragma warning restore 1591
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDocument_UnsupportedChecksumAlgorithm_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var checksumBytes = new byte[] { (byte)'t', (byte)'e', (byte)'s', (byte)'t', };
|
||||
|
||||
var sourceDocument = Mock.Of<RazorSourceDocument>(d =>
|
||||
d.FilePath == "test.cshtml" &&
|
||||
d.GetChecksum() == checksumBytes &&
|
||||
d.GetChecksumAlgorithm() == "SHA3");
|
||||
|
||||
var document = new DocumentIntermediateNode();
|
||||
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var target = CodeTarget.CreateDefault(codeDocument, options);
|
||||
var writer = new DefaultDocumentWriter(target, options);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
var result = writer.WriteDocument(codeDocument, document);
|
||||
});
|
||||
Assert.Equal(
|
||||
"The hash algorithm 'SHA3' is not supported for checksum generation. Supported algorithms are: 'SHA1 SHA256'. " +
|
||||
"Set 'RazorCodeGenerationOptions.SuppressChecksum' to 'True' to suppress automatic checksum generation.",
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public void WriteDocument_Empty_SuppressChecksumTrue_DoesnotWriteChecksum()
|
||||
{
|
||||
// Arrange
|
||||
var document = new DocumentIntermediateNode();
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var optionsBuilder = new DefaultRazorCodeGenerationOptionsBuilder(designTime: false)
|
||||
{
|
||||
SuppressChecksum = true
|
||||
};
|
||||
var options = optionsBuilder.Build();
|
||||
|
||||
var target = CodeTarget.CreateDefault(codeDocument, options);
|
||||
var writer = new DefaultDocumentWriter(target, options);
|
||||
|
||||
// Act
|
||||
var result = writer.WriteDocument(codeDocument, document);
|
||||
|
||||
// Assert
|
||||
var csharp = result.GeneratedCode;
|
||||
Assert.Equal(
|
||||
@"// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
#pragma warning restore 1591
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDocument_WritesNamespace()
|
||||
{
|
||||
// Arrange
|
||||
var document = new DocumentIntermediateNode();
|
||||
var builder = IntermediateNodeBuilder.Create(document);
|
||||
builder.Add(new NamespaceDeclarationIntermediateNode()
|
||||
{
|
||||
Content = "TestNamespace",
|
||||
});
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var target = CodeTarget.CreateDefault(codeDocument, options);
|
||||
var writer = new DefaultDocumentWriter(target, options);
|
||||
|
||||
// Act
|
||||
var result = writer.WriteDocument(codeDocument, document);
|
||||
|
||||
// Assert
|
||||
var csharp = result.GeneratedCode;
|
||||
Assert.Equal(
|
||||
@"#pragma checksum ""test.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""da39a3ee5e6b4b0d3255bfef95601890afd80709""
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
namespace TestNamespace
|
||||
{
|
||||
#line hidden
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDocument_WritesClass()
|
||||
{
|
||||
// Arrange
|
||||
var document = new DocumentIntermediateNode();
|
||||
var builder = IntermediateNodeBuilder.Create(document);
|
||||
builder.Add(new ClassDeclarationIntermediateNode()
|
||||
{
|
||||
Modifiers =
|
||||
{
|
||||
"internal"
|
||||
},
|
||||
BaseType = "TestBase",
|
||||
Interfaces = new List<string> { "IFoo", "IBar", },
|
||||
TypeParameters = new List<TypeParameter>
|
||||
{
|
||||
new TypeParameter() { ParameterName = "TKey", },
|
||||
new TypeParameter() { ParameterName = "TValue", },
|
||||
},
|
||||
ClassName = "TestClass",
|
||||
});
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var target = CodeTarget.CreateDefault(codeDocument, options);
|
||||
var writer = new DefaultDocumentWriter(target, options);
|
||||
|
||||
// Act
|
||||
var result = writer.WriteDocument(codeDocument, document);
|
||||
|
||||
// Assert
|
||||
var csharp = result.GeneratedCode;
|
||||
Assert.Equal(
|
||||
@"#pragma checksum ""test.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""da39a3ee5e6b4b0d3255bfef95601890afd80709""
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
internal class TestClass<TKey, TValue> : TestBase, IFoo, IBar
|
||||
{
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDocument_WritesMethod()
|
||||
{
|
||||
// Arrange
|
||||
var document = new DocumentIntermediateNode();
|
||||
var builder = IntermediateNodeBuilder.Create(document);
|
||||
builder.Add(new MethodDeclarationIntermediateNode()
|
||||
{
|
||||
Modifiers =
|
||||
{
|
||||
"internal",
|
||||
"virtual",
|
||||
"async",
|
||||
},
|
||||
MethodName = "TestMethod",
|
||||
Parameters =
|
||||
{
|
||||
new MethodParameter()
|
||||
{
|
||||
Modifiers =
|
||||
{
|
||||
"readonly",
|
||||
"ref",
|
||||
},
|
||||
ParameterName = "a",
|
||||
TypeName = "int",
|
||||
},
|
||||
new MethodParameter()
|
||||
{
|
||||
ParameterName = "b",
|
||||
TypeName = "string",
|
||||
}
|
||||
},
|
||||
ReturnType = "string",
|
||||
});
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var target = CodeTarget.CreateDefault(codeDocument, options);
|
||||
var writer = new DefaultDocumentWriter(target, options);
|
||||
|
||||
// Act
|
||||
var result = writer.WriteDocument(codeDocument, document);
|
||||
|
||||
// Assert
|
||||
var csharp = result.GeneratedCode;
|
||||
Assert.Equal(
|
||||
@"#pragma checksum ""test.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""da39a3ee5e6b4b0d3255bfef95601890afd80709""
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
#pragma warning disable 1998
|
||||
internal virtual async string TestMethod(readonly ref int a, string b)
|
||||
{
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
#pragma warning restore 1591
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDocument_WritesField()
|
||||
{
|
||||
// Arrange
|
||||
var document = new DocumentIntermediateNode();
|
||||
var builder = IntermediateNodeBuilder.Create(document);
|
||||
builder.Add(new FieldDeclarationIntermediateNode()
|
||||
{
|
||||
Modifiers =
|
||||
{
|
||||
"internal",
|
||||
"readonly",
|
||||
},
|
||||
FieldName = "_foo",
|
||||
FieldType = "string",
|
||||
});
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var target = CodeTarget.CreateDefault(codeDocument, options);
|
||||
var writer = new DefaultDocumentWriter(target, options);
|
||||
|
||||
// Act
|
||||
var result = writer.WriteDocument(codeDocument, document);
|
||||
|
||||
// Assert
|
||||
var csharp = result.GeneratedCode;
|
||||
Assert.Equal(
|
||||
@"#pragma checksum ""test.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""da39a3ee5e6b4b0d3255bfef95601890afd80709""
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
internal readonly string _foo;
|
||||
#pragma warning restore 1591
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDocument_WritesProperty()
|
||||
{
|
||||
// Arrange
|
||||
var document = new DocumentIntermediateNode();
|
||||
var builder = IntermediateNodeBuilder.Create(document);
|
||||
builder.Add(new PropertyDeclarationIntermediateNode()
|
||||
{
|
||||
Modifiers =
|
||||
{
|
||||
"internal",
|
||||
"virtual",
|
||||
},
|
||||
PropertyName = "Foo",
|
||||
PropertyType = "string",
|
||||
});
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
var target = CodeTarget.CreateDefault(codeDocument, options);
|
||||
var writer = new DefaultDocumentWriter(target, options);
|
||||
|
||||
// Act
|
||||
var result = writer.WriteDocument(codeDocument, document);
|
||||
|
||||
// Assert
|
||||
var csharp = result.GeneratedCode;
|
||||
Assert.Equal(
|
||||
@"#pragma checksum ""test.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""da39a3ee5e6b4b0d3255bfef95601890afd80709""
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
internal virtual string Foo { get; set; }
|
||||
#pragma warning restore 1591
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,498 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
{
|
||||
public class DesignTimeNodeWriterTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Latest;
|
||||
|
||||
[Fact]
|
||||
public void WriteUsingDirective_NoSource_WritesContent()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new UsingDirectiveIntermediateNode()
|
||||
{
|
||||
Content = "System",
|
||||
};
|
||||
|
||||
// Act
|
||||
writer.WriteUsingDirective(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"using System;
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUsingDirective_WithSource_WritesContentWithLinePragmaAndMapping()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var originalSpan = new SourceSpan("test.cshtml", 0, 0, 0, 6);
|
||||
var generatedSpan = new SourceSpan(null, 38 + Environment.NewLine.Length * 3, 3, 0, 6);
|
||||
var expectedSourceMapping = new SourceMapping(originalSpan, generatedSpan);
|
||||
var node = new UsingDirectiveIntermediateNode()
|
||||
{
|
||||
Content = "System",
|
||||
Source = originalSpan,
|
||||
};
|
||||
|
||||
// Act
|
||||
writer.WriteUsingDirective(context, node);
|
||||
|
||||
// Assert
|
||||
var mapping = Assert.Single(((DefaultCodeRenderingContext)context).SourceMappings);
|
||||
Assert.Equal(expectedSourceMapping, mapping);
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
using System;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpExpression_SkipsLinePragma_WithoutSource()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new CSharpExpressionIntermediateNode();
|
||||
var builder = IntermediateNodeBuilder.Create(node);
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "i++",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpression(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"__o = i++;
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpExpression_WritesLinePragma_WithSource()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new CSharpExpressionIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 3),
|
||||
};
|
||||
var builder = IntermediateNodeBuilder.Create(node);
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "i++",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpression(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
__o = i++;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpExpression_WithExtensionNode_WritesPadding()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new CSharpExpressionIntermediateNode();
|
||||
var builder = IntermediateNodeBuilder.Create(node);
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "i",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
builder.Add(new MyExtensionIntermediateNode());
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "++",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpression(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"__o = iRender Children
|
||||
++;
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpExpression_WithSource_WritesPadding()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new CSharpExpressionIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 8, 0, 8, 3),
|
||||
};
|
||||
var builder = IntermediateNodeBuilder.Create(node);
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "i",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
builder.Add(new MyExtensionIntermediateNode());
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "++",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpression(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
__o = iRender Children
|
||||
++;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCode_WhitespaceContentWithSource_WritesContent()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new CSharpCodeIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 3),
|
||||
};
|
||||
IntermediateNodeBuilder.Create(node)
|
||||
.Add(new IntermediateToken()
|
||||
{
|
||||
Kind = TokenKind.CSharp,
|
||||
Content = " "
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCode(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCode_SkipsLinePragma_WithoutSource()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new CSharpCodeIntermediateNode();
|
||||
IntermediateNodeBuilder.Create(node)
|
||||
.Add(new IntermediateToken()
|
||||
{
|
||||
Kind = TokenKind.CSharp,
|
||||
Content = "if (true) { }"
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCode(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"if (true) { }
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCode_WritesLinePragma_WithSource()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new CSharpCodeIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 13),
|
||||
};
|
||||
IntermediateNodeBuilder.Create(node)
|
||||
.Add(new IntermediateToken()
|
||||
{
|
||||
Kind = TokenKind.CSharp,
|
||||
Content = "if (true) { }",
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCode(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
if (true) { }
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCode_WritesPadding_WithSource()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new CSharpCodeIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 17),
|
||||
};
|
||||
IntermediateNodeBuilder.Create(node)
|
||||
.Add(new IntermediateToken()
|
||||
{
|
||||
Kind = TokenKind.CSharp,
|
||||
Content = " if (true) { }",
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCode(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
if (true) { }
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpExpressionAttributeValue_RendersCorrectly()
|
||||
{
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
|
||||
var content = "<input checked=\"hello-world @false\" />";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var documentNode = Lower(codeDocument);
|
||||
var node = documentNode.Children.OfType<HtmlAttributeIntermediateNode>().Single().Children[1] as CSharpExpressionAttributeValueIntermediateNode;
|
||||
|
||||
var context = TestCodeRenderingContext.CreateDesignTime(source: sourceDocument);
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpressionAttributeValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
__o = false;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCodeAttributeValue_RendersCorrectly()
|
||||
{
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var content = "<input checked=\"hello-world @if(@true){ }\" />";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var documentNode = Lower(codeDocument);
|
||||
var node = documentNode.Children.OfType<HtmlAttributeIntermediateNode>().Single().Children[1] as CSharpCodeAttributeValueIntermediateNode;
|
||||
|
||||
var context = TestCodeRenderingContext.CreateDesignTime(source: sourceDocument);
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCodeAttributeValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
if(@true){ }
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCodeAttributeValue_WithExpression_RendersCorrectly()
|
||||
{
|
||||
var writer = new DesignTimeNodeWriter();
|
||||
var content = "<input checked=\"hello-world @if(@true){ @false }\" />";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var documentNode = Lower(codeDocument);
|
||||
var node = documentNode.Children.OfType<HtmlAttributeIntermediateNode>().Single().Children[1] as CSharpCodeAttributeValueIntermediateNode;
|
||||
|
||||
var context = TestCodeRenderingContext.CreateDesignTime(source: sourceDocument);
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCodeAttributeValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
if(@true){
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
Render Children
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode Lower(RazorCodeDocument codeDocument)
|
||||
{
|
||||
var projectEngine = CreateProjectEngine();
|
||||
return Lower(codeDocument, projectEngine);
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode Lower(RazorCodeDocument codeDocument, RazorProjectEngine projectEngine)
|
||||
{
|
||||
for (var i = 0; i < projectEngine.Phases.Count; i++)
|
||||
{
|
||||
var phase = projectEngine.Phases[i];
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
if (phase is IRazorIntermediateNodeLoweringPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
Assert.NotNull(documentNode);
|
||||
|
||||
return documentNode;
|
||||
}
|
||||
|
||||
private class MyExtensionIntermediateNode : ExtensionIntermediateNode
|
||||
{
|
||||
public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly;
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
{
|
||||
visitor.VisitDefault(this);
|
||||
}
|
||||
|
||||
public override void WriteNode(CodeTarget target, CodeRenderingContext context)
|
||||
{
|
||||
context.CodeWriter.WriteLine("MyExtensionNode");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
{
|
||||
public class LiteralRuntimeNodeWriterTest
|
||||
{
|
||||
[Fact]
|
||||
public void WriteCSharpExpression_UsesWriteLiteral_WritesLinePragma_WithSource()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new LiteralRuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new CSharpExpressionIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 3),
|
||||
};
|
||||
var builder = IntermediateNodeBuilder.Create(node);
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "i++",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpression(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
WriteLiteral(i++);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,705 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
{
|
||||
public class RuntimeNodeWriterTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Latest;
|
||||
|
||||
[Fact]
|
||||
public void WriteUsingDirective_NoSource_WritesContent()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new UsingDirectiveIntermediateNode()
|
||||
{
|
||||
Content = "System",
|
||||
};
|
||||
|
||||
// Act
|
||||
writer.WriteUsingDirective(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"using System;
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUsingDirective_WithSource_WritesContentWithLinePragma()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new UsingDirectiveIntermediateNode()
|
||||
{
|
||||
Content = "System",
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 3),
|
||||
};
|
||||
|
||||
// Act
|
||||
writer.WriteUsingDirective(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
using System;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpExpression_SkipsLinePragma_WithoutSource()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter()
|
||||
{
|
||||
WriteCSharpExpressionMethod = "Test",
|
||||
};
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new CSharpExpressionIntermediateNode();
|
||||
var builder = IntermediateNodeBuilder.Create(node);
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "i++",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpression(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"Test(i++);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpExpression_WritesLinePragma_WithSource()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter()
|
||||
{
|
||||
WriteCSharpExpressionMethod = "Test",
|
||||
};
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new CSharpExpressionIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 3),
|
||||
};
|
||||
var builder = IntermediateNodeBuilder.Create(node);
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "i++",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpression(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
Test(i++);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpExpression_WithExtensionNode_WritesPadding()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter()
|
||||
{
|
||||
WriteCSharpExpressionMethod = "Test",
|
||||
};
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new CSharpExpressionIntermediateNode();
|
||||
var builder = IntermediateNodeBuilder.Create(node);
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "i",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
builder.Add(new MyExtensionIntermediateNode());
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "++",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpression(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"Test(iRender Children
|
||||
++);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpExpression_WithSource_WritesPadding()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter()
|
||||
{
|
||||
WriteCSharpExpressionMethod = "Test",
|
||||
};
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new CSharpExpressionIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 8, 0, 8, 3),
|
||||
};
|
||||
var builder = IntermediateNodeBuilder.Create(node);
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "i",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
builder.Add(new MyExtensionIntermediateNode());
|
||||
builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "++",
|
||||
Kind = TokenKind.CSharp,
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpression(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
Test(iRender Children
|
||||
++);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCode_WhitespaceContent_DoesNothing()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new CSharpCodeIntermediateNode();
|
||||
IntermediateNodeBuilder.Create(node)
|
||||
.Add(new IntermediateToken()
|
||||
{
|
||||
Kind = TokenKind.CSharp,
|
||||
Content = " \t"
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCode(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Empty(csharp);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCode_SkipsLinePragma_WithoutSource()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new CSharpCodeIntermediateNode();
|
||||
IntermediateNodeBuilder.Create(node)
|
||||
.Add(new IntermediateToken()
|
||||
{
|
||||
Kind = TokenKind.CSharp,
|
||||
Content = "if (true) { }"
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCode(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"if (true) { }
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCode_WritesLinePragma_WithSource()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new CSharpCodeIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 13),
|
||||
};
|
||||
IntermediateNodeBuilder.Create(node)
|
||||
.Add(new IntermediateToken()
|
||||
{
|
||||
Kind = TokenKind.CSharp,
|
||||
Content = "if (true) { }",
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCode(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
if (true) { }
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCode_WritesPadding_WithSource()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new CSharpCodeIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 17),
|
||||
};
|
||||
IntermediateNodeBuilder.Create(node)
|
||||
.Add(new IntermediateToken()
|
||||
{
|
||||
Kind = TokenKind.CSharp,
|
||||
Content = " if (true) { }",
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCode(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
if (true) { }
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHtmlLiteral_WithinMaxSize_WritesSingleLiteral()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
writer.WriteHtmlLiteral(context, maxStringLiteralLength: 6, "Hello");
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"WriteLiteral(""Hello"");
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHtmlLiteral_GreaterThanMaxSize_WritesMultipleLiterals()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
writer.WriteHtmlLiteral(context, maxStringLiteralLength: 6, "Hello World");
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"WriteLiteral(""Hello "");
|
||||
WriteLiteral(""World"");
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHtmlLiteral_GreaterThanMaxSize_SingleEmojisSplit()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
writer.WriteHtmlLiteral(context, maxStringLiteralLength: 2, " 👦");
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"WriteLiteral("" "");
|
||||
WriteLiteral(""👦"");
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHtmlLiteral_GreaterThanMaxSize_SequencedZeroWithJoinedEmojisSplit()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
writer.WriteHtmlLiteral(context, maxStringLiteralLength: 6, "👩👩👧👧👩👩👧👧");
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"WriteLiteral(""👩👩"");
|
||||
WriteLiteral(""👧👧"");
|
||||
WriteLiteral(""👩👩"");
|
||||
WriteLiteral(""👧👧"");
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHtmlContent_RendersContentCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new HtmlContentIntermediateNode();
|
||||
node.Children.Add(new IntermediateToken()
|
||||
{
|
||||
Content = "SomeContent",
|
||||
Kind = TokenKind.Html,
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteHtmlContent(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"WriteLiteral(""SomeContent"");
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHtmlContent_LargeStringLiteral_UsesMultipleWrites()
|
||||
{
|
||||
// Arrange
|
||||
var codeWriter = new CodeWriter();
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new HtmlContentIntermediateNode();
|
||||
node.Children.Add(new IntermediateToken()
|
||||
{
|
||||
Content = new string('*', 2000),
|
||||
Kind = TokenKind.Html,
|
||||
});
|
||||
|
||||
// Act
|
||||
writer.WriteHtmlContent(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(string.Format(
|
||||
@"WriteLiteral(@""{0}"");
|
||||
WriteLiteral(@""{1}"");
|
||||
", new string('*', 1024), new string('*', 976)),
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHtmlAttribute_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var content = "<input checked=\"hello-world @false\" />";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var documentNode = Lower(codeDocument);
|
||||
var node = documentNode.Children.OfType<HtmlAttributeIntermediateNode>().Single();
|
||||
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
writer.WriteHtmlAttribute(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"BeginWriteAttribute(""checked"", "" checked=\"""", 6, ""\"""", 34, 2);
|
||||
Render Children
|
||||
Render Children
|
||||
EndWriteAttribute();
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHtmlAttributeValue_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var content = "<input checked=\"hello-world @false\" />";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var documentNode = Lower(codeDocument);
|
||||
var node = documentNode.Children.OfType<HtmlAttributeIntermediateNode>().Single().Children[0] as HtmlAttributeValueIntermediateNode;
|
||||
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
writer.WriteHtmlAttributeValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"WriteAttributeValue("""", 16, ""hello-world"", 16, 11, true);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpExpressionAttributeValue_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new RuntimeNodeWriter();
|
||||
var content = "<input checked=\"hello-world @false\" />";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var documentNode = Lower(codeDocument);
|
||||
var node = documentNode.Children.OfType<HtmlAttributeIntermediateNode>().Single().Children[1] as CSharpExpressionAttributeValueIntermediateNode;
|
||||
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpressionAttributeValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
WriteAttributeValue("" "", 27, false, 28, 6, false);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCodeAttributeValue_BuffersResult()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new RuntimeNodeWriter();
|
||||
|
||||
var content = "<input checked=\"hello-world @if(@true){ }\" />";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var documentNode = Lower(codeDocument);
|
||||
var node = documentNode.Children.OfType<HtmlAttributeIntermediateNode>().Single().Children[1] as CSharpCodeAttributeValueIntermediateNode;
|
||||
|
||||
var context = TestCodeRenderingContext.CreateRuntime(source: sourceDocument);
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCodeAttributeValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"WriteAttributeValue("" "", 27, new Microsoft.AspNetCore.Mvc.Razor.HelperResult(async(__razor_attribute_value_writer) => {
|
||||
PushWriter(__razor_attribute_value_writer);
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
if(@true){ }
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
PopWriter();
|
||||
}
|
||||
), 28, 13, false);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BeginWriterScope_UsesSpecifiedWriter_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new RuntimeNodeWriter()
|
||||
{
|
||||
PushWriterMethod = "TestPushWriter"
|
||||
};
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
writer.BeginWriterScope(context, "MyWriter");
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"TestPushWriter(MyWriter);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndWriterScope_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new RuntimeNodeWriter()
|
||||
{
|
||||
PopWriterMethod = "TestPopWriter"
|
||||
};
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
writer.EndWriterScope(context);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"TestPopWriter();
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode Lower(RazorCodeDocument codeDocument)
|
||||
{
|
||||
var projectEngine = CreateProjectEngine();
|
||||
return Lower(codeDocument, projectEngine);
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode Lower(RazorCodeDocument codeDocument, RazorProjectEngine projectEngine)
|
||||
{
|
||||
for (var i = 0; i < projectEngine.Phases.Count; i++)
|
||||
{
|
||||
var phase = projectEngine.Phases[i];
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
if (phase is IRazorIntermediateNodeLoweringPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var irDocument = codeDocument.GetDocumentIntermediateNode();
|
||||
Assert.NotNull(irDocument);
|
||||
|
||||
return irDocument;
|
||||
}
|
||||
|
||||
private class MyExtensionIntermediateNode : ExtensionIntermediateNode
|
||||
{
|
||||
public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly;
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
{
|
||||
visitor.VisitDefault(this);
|
||||
}
|
||||
|
||||
public override void WriteNode(CodeTarget target, CodeRenderingContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
{
|
||||
public class TagHelperHtmlAttributeRuntimeNodeWriterTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Latest;
|
||||
|
||||
[Fact]
|
||||
public void WriteHtmlAttributeValue_RendersCorrectly()
|
||||
{
|
||||
var writer = new TagHelperHtmlAttributeRuntimeNodeWriter();
|
||||
|
||||
var content = "<input checked=\"hello-world @false\" />";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var documentNode = Lower(codeDocument);
|
||||
var node = documentNode.Children.OfType<HtmlAttributeIntermediateNode>().Single().Children[0] as HtmlAttributeValueIntermediateNode;
|
||||
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
writer.WriteHtmlAttributeValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"AddHtmlAttributeValue("""", 16, ""hello-world"", 16, 11, true);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpExpressionAttributeValue_RendersCorrectly()
|
||||
{
|
||||
var writer = new TagHelperHtmlAttributeRuntimeNodeWriter();
|
||||
var content = "<input checked=\"hello-world @false\" />";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var documentNode = Lower(codeDocument);
|
||||
var node = documentNode.Children.OfType<HtmlAttributeIntermediateNode>().Single().Children[1] as CSharpExpressionAttributeValueIntermediateNode;
|
||||
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpExpressionAttributeValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
AddHtmlAttributeValue("" "", 27, false, 28, 6, false);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCSharpCodeAttributeValue_BuffersResult()
|
||||
{
|
||||
var writer = new TagHelperHtmlAttributeRuntimeNodeWriter();
|
||||
|
||||
var content = "<input checked=\"hello-world @if(@true){ }\" />";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var documentNode = Lower(codeDocument);
|
||||
var node = documentNode.Children.OfType<HtmlAttributeIntermediateNode>().Single().Children[1] as CSharpCodeAttributeValueIntermediateNode;
|
||||
|
||||
var context = TestCodeRenderingContext.CreateRuntime(source: sourceDocument);
|
||||
|
||||
// Act
|
||||
writer.WriteCSharpCodeAttributeValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"AddHtmlAttributeValue("" "", 27, new Microsoft.AspNetCore.Mvc.Razor.HelperResult(async(__razor_attribute_value_writer) => {
|
||||
PushWriter(__razor_attribute_value_writer);
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
if(@true){ }
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
PopWriter();
|
||||
}
|
||||
), 28, 13, false);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode Lower(RazorCodeDocument codeDocument)
|
||||
{
|
||||
var projectEngine = CreateProjectEngine();
|
||||
return Lower(codeDocument, projectEngine);
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode Lower(RazorCodeDocument codeDocument, RazorProjectEngine projectEngine)
|
||||
{
|
||||
for (var i = 0; i < projectEngine.Phases.Count; i++)
|
||||
{
|
||||
var phase = projectEngine.Phases[i];
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
if (phase is IRazorIntermediateNodeLoweringPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
Assert.NotNull(documentNode);
|
||||
|
||||
return documentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
public class ComponentDocumentClassifierPassTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Latest;
|
||||
|
||||
[Fact]
|
||||
public void Execute_SetsDocumentKind()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("some-content", "Test.razor"));
|
||||
codeDocument.SetFileKind(FileKinds.Component);
|
||||
|
||||
var projectEngine = CreateProjectEngine();
|
||||
var irDocument = CreateIRDocument(projectEngine, codeDocument);
|
||||
var pass = new ComponentDocumentClassifierPass
|
||||
{
|
||||
Engine = projectEngine.Engine
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ComponentDocumentClassifierPass.ComponentDocumentKind, irDocument.DocumentKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComponentDocumentClassifierPass_SetsNamespace()
|
||||
{
|
||||
// Arrange
|
||||
var properties = new RazorSourceDocumentProperties(filePath: "/MyApp/Test.razor", relativePath: "Test.razor");
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("some-content", properties));
|
||||
codeDocument.SetFileKind(FileKinds.Component);
|
||||
|
||||
var projectEngine = CreateProjectEngine(b =>
|
||||
{
|
||||
b.SetRootNamespace("MyApp");
|
||||
});
|
||||
|
||||
var irDocument = CreateIRDocument(projectEngine, codeDocument);
|
||||
var pass = new ComponentDocumentClassifierPass
|
||||
{
|
||||
Engine = projectEngine.Engine
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
var visitor = new Visitor();
|
||||
visitor.Visit(irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("MyApp", visitor.Namespace.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComponentDocumentClassifierPass_SetsClass()
|
||||
{
|
||||
// Arrange
|
||||
var properties = new RazorSourceDocumentProperties(filePath: "/MyApp/Test.razor", relativePath: "Test.razor");
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("some-content", properties));
|
||||
codeDocument.SetFileKind(FileKinds.Component);
|
||||
|
||||
var projectEngine = CreateProjectEngine(b =>
|
||||
{
|
||||
b.SetRootNamespace("MyApp");
|
||||
});
|
||||
|
||||
var irDocument = CreateIRDocument(projectEngine, codeDocument);
|
||||
var pass = new ComponentDocumentClassifierPass
|
||||
{
|
||||
Engine = projectEngine.Engine
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
var visitor = new Visitor();
|
||||
visitor.Visit(irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal($"{CodeGenerationConstants.ComponentBase.FullTypeName}", visitor.Class.BaseType);
|
||||
Assert.Equal(new[] { "public", }, visitor.Class.Modifiers);
|
||||
Assert.Equal("Test", visitor.Class.ClassName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComponentDocumentClassifierPass_UsesRelativePathToGenerateTypeNameAndNamespace()
|
||||
{
|
||||
// Arrange
|
||||
var relativePath = "/Pages/Announcements/Banner.razor";
|
||||
var properties = new RazorSourceDocumentProperties(filePath: $"/MyApp{relativePath}", relativePath: relativePath);
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("some-content", properties));
|
||||
codeDocument.SetFileKind(FileKinds.Component);
|
||||
|
||||
var projectEngine = CreateProjectEngine(b =>
|
||||
{
|
||||
b.SetRootNamespace("MyApp");
|
||||
});
|
||||
|
||||
var irDocument = CreateIRDocument(projectEngine, codeDocument);
|
||||
var pass = new ComponentDocumentClassifierPass
|
||||
{
|
||||
Engine = projectEngine.Engine
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
var visitor = new Visitor();
|
||||
visitor.Visit(irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Banner", visitor.Class.ClassName);
|
||||
Assert.Equal("MyApp.Pages.Announcements", visitor.Namespace.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComponentDocumentClassifierPass_SanitizesClassName()
|
||||
{
|
||||
// Arrange
|
||||
var properties = new RazorSourceDocumentProperties(filePath: @"x:\My.+App\path.with+invalid-chars.razor", relativePath: "path.with+invalid-chars.razor");
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("some-content", properties));
|
||||
codeDocument.SetFileKind(FileKinds.Component);
|
||||
|
||||
var projectEngine = CreateProjectEngine(b =>
|
||||
{
|
||||
b.SetRootNamespace("My.+App");
|
||||
});
|
||||
|
||||
var irDocument = CreateIRDocument(projectEngine, codeDocument);
|
||||
var pass = new ComponentDocumentClassifierPass
|
||||
{
|
||||
Engine = projectEngine.Engine
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
var visitor = new Visitor();
|
||||
visitor.Visit(irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("path_with_invalid_chars", visitor.Class.ClassName);
|
||||
Assert.Equal("My._App", visitor.Namespace.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComponentDocumentClassifierPass_SetsUpMainMethod()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("some-content", "Test.razor"));
|
||||
codeDocument.SetFileKind(FileKinds.Component);
|
||||
|
||||
var projectEngine = CreateProjectEngine();
|
||||
var irDocument = CreateIRDocument(projectEngine, codeDocument);
|
||||
var pass = new ComponentDocumentClassifierPass
|
||||
{
|
||||
Engine = projectEngine.Engine
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
var visitor = new Visitor();
|
||||
visitor.Visit(irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(CodeGenerationConstants.ComponentBase.BuildRenderTree, visitor.Method.MethodName);
|
||||
Assert.Equal("void", visitor.Method.ReturnType);
|
||||
Assert.Equal(new[] { "protected", "override" }, visitor.Method.Modifiers);
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode CreateIRDocument(RazorProjectEngine projectEngine, RazorCodeDocument codeDocument)
|
||||
{
|
||||
for (var i = 0; i < projectEngine.Phases.Count; i++)
|
||||
{
|
||||
var phase = projectEngine.Phases[i];
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
if (phase is IRazorIntermediateNodeLoweringPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return codeDocument.GetDocumentIntermediateNode();
|
||||
}
|
||||
|
||||
private class Visitor : IntermediateNodeWalker
|
||||
{
|
||||
public NamespaceDeclarationIntermediateNode Namespace { get; private set; }
|
||||
|
||||
public ClassDeclarationIntermediateNode Class { get; private set; }
|
||||
|
||||
public MethodDeclarationIntermediateNode Method { get; private set; }
|
||||
|
||||
public override void VisitMethodDeclaration(MethodDeclarationIntermediateNode node)
|
||||
{
|
||||
Method = node;
|
||||
}
|
||||
|
||||
public override void VisitNamespaceDeclaration(NamespaceDeclarationIntermediateNode node)
|
||||
{
|
||||
Namespace = node;
|
||||
base.VisitNamespaceDeclaration(node);
|
||||
}
|
||||
|
||||
public override void VisitClassDeclaration(ClassDeclarationIntermediateNode node)
|
||||
{
|
||||
Class = node;
|
||||
base.VisitClassDeclaration(node);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestComponentDocumentClassifierPass : ComponentDocumentClassifierPass
|
||||
{
|
||||
public new bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
=> base.IsMatch(codeDocument, documentNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,403 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
public class ComponentMarkupBlockPassTest
|
||||
{
|
||||
public ComponentMarkupBlockPassTest()
|
||||
{
|
||||
Pass = new ComponentMarkupBlockPass();
|
||||
ProjectEngine = (DefaultRazorProjectEngine)RazorProjectEngine.Create(
|
||||
RazorConfiguration.Default,
|
||||
RazorProjectFileSystem.Create(Environment.CurrentDirectory),
|
||||
b =>
|
||||
{
|
||||
if (b.Features.OfType<ComponentMarkupBlockPass>().Any())
|
||||
{
|
||||
b.Features.Remove(b.Features.OfType<ComponentMarkupBlockPass>().Single());
|
||||
}
|
||||
});
|
||||
Engine = ProjectEngine.Engine;
|
||||
|
||||
Pass.Engine = Engine;
|
||||
}
|
||||
|
||||
private DefaultRazorProjectEngine ProjectEngine { get; }
|
||||
|
||||
private RazorEngine Engine { get; }
|
||||
|
||||
private ComponentMarkupBlockPass Pass { get; }
|
||||
|
||||
[Fact]
|
||||
public void Execute_RewritesHtml_Basic()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<html>
|
||||
<head cool=""beans"">
|
||||
Hello, World!
|
||||
</head>
|
||||
</html>");
|
||||
|
||||
var expected = NormalizeContent(@"
|
||||
<html>
|
||||
<head cool=""beans"">
|
||||
Hello, World!
|
||||
</head>
|
||||
</html>");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var block = documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>().Single();
|
||||
Assert.Equal(expected, block.Content, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_RewritesHtml_WithComment()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"Start<!-- -->End");
|
||||
|
||||
var expected = NormalizeContent(@"StartEnd");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var block = documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>().Single();
|
||||
Assert.Equal(expected, block.Content, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
// See: https://github.com/aspnet/AspNetCore/issues/6480
|
||||
[Fact]
|
||||
public void Execute_RewritesHtml_HtmlAttributePrefix()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"<div class=""one two"">Hi</div>");
|
||||
|
||||
var expected = NormalizeContent(@"<div class=""one two"">Hi</div>");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var block = documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>().Single();
|
||||
Assert.Equal(expected, block.Content, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_RewritesHtml_MergesSiblings()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<html>
|
||||
@(""Hi"")<div></div>
|
||||
<div></div>
|
||||
<div>@(""Hi"")</div>
|
||||
</html>");
|
||||
|
||||
var expected = NormalizeContent(@"
|
||||
<div></div>
|
||||
<div></div>
|
||||
");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var block = documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>().Single();
|
||||
Assert.Equal(expected, block.Content, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_RewritesHtml_MergesSiblings_LeftEdge()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<html><div></div>
|
||||
<div></div>
|
||||
<div>@(""Hi"")</div>
|
||||
</html>");
|
||||
|
||||
var expected = NormalizeContent(@"
|
||||
<div></div>
|
||||
<div></div>
|
||||
");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var block = documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>().Single();
|
||||
Assert.Equal(expected, block.Content, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Execute_RewritesHtml_CSharpInAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<html>
|
||||
<head cool=""beans"" csharp=""@yes"" mixed=""hi @there"">
|
||||
<div>foo</div>
|
||||
</head>
|
||||
</html>");
|
||||
|
||||
var expected = NormalizeContent("<div>foo</div>\n ");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var block = documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>().Single();
|
||||
Assert.Equal(expected, block.Content, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_RewritesHtml_CSharpInBody()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<html>
|
||||
<head cool=""beans"">
|
||||
<div>@foo</div>
|
||||
<div>rewriteme</div>
|
||||
<div>@bar</div>
|
||||
</head>
|
||||
</html>");
|
||||
|
||||
var expected = NormalizeContent("<div>rewriteme</div>\n ");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var block = documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>().Single();
|
||||
Assert.Equal(expected, block.Content, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_RewritesHtml_EncodesHtmlEntities()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<div>
|
||||
<span>Hi</span>
|
||||
</div>");
|
||||
|
||||
var expected = NormalizeContent(@"
|
||||
<div>
|
||||
<span>Hi</span>
|
||||
</div>");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var block = documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>().Single();
|
||||
Assert.Equal(expected, block.Content, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_RewritesHtml_EmptyNonvoid()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"<a href=""...""></a>");
|
||||
|
||||
var expected = NormalizeContent(@"<a href=""...""></a>");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var block = documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>().Single();
|
||||
Assert.Equal(expected, block.Content, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_RewritesHtml_Void()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"<link rel=""..."" href=""...""/>");
|
||||
|
||||
var expected = NormalizeContent(@"<link rel=""..."" href=""..."">");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var block = documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>().Single();
|
||||
Assert.Equal(expected, block.Content, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_CannotRewriteHtml_CSharpInCode()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<html>
|
||||
@if (some_bool)
|
||||
{
|
||||
<head cool=""beans"">
|
||||
@hello
|
||||
</head>
|
||||
}
|
||||
</html>");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_CannotRewriteHtml_Script()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<html>
|
||||
@if (some_bool)
|
||||
{
|
||||
<head cool=""beans"">
|
||||
<script>...</script>
|
||||
</head>
|
||||
}
|
||||
</html>");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>());
|
||||
}
|
||||
|
||||
// The unclosed tag will have errors, so we won't rewrite it or its parent.
|
||||
[Fact]
|
||||
public void Execute_CannotRewriteHtml_Errors()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<html>
|
||||
<a href=""..."">
|
||||
</html>");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_RewritesHtml_MismatchedClosingTag()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<html>
|
||||
<div>
|
||||
<div>rewriteme</div>
|
||||
</span>
|
||||
</html>");
|
||||
|
||||
var expected = NormalizeContent("<div>rewriteme</div>\n ");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var block = documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>().Single();
|
||||
Assert.Equal(expected, block.Content, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
private string NormalizeContent(string content)
|
||||
{
|
||||
// Test inputs frequently have leading space for readability.
|
||||
content = content.TrimStart();
|
||||
|
||||
// Normalize newlines since we are testing lengths of things.
|
||||
content = content.Replace("\r", "");
|
||||
content = content.Replace("\n", "\r\n");
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
private RazorCodeDocument CreateDocument(string content)
|
||||
{
|
||||
// Normalize newlines since we are testing lengths of things.
|
||||
content = content.Replace("\r", "");
|
||||
content = content.Replace("\n", "\r\n");
|
||||
|
||||
var source = RazorSourceDocument.Create(content, "test.cshtml");
|
||||
return ProjectEngine.CreateCodeDocumentCore(source, FileKinds.Component);
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode Lower(RazorCodeDocument codeDocument)
|
||||
{
|
||||
for (var i = 0; i < Engine.Phases.Count; i++)
|
||||
{
|
||||
var phase = Engine.Phases[i];
|
||||
if (phase is IRazorCSharpLoweringPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
phase.Execute(codeDocument);
|
||||
}
|
||||
|
||||
var document = codeDocument.GetDocumentIntermediateNode();
|
||||
Engine.Features.OfType<ComponentDocumentClassifierPass>().Single().Execute(codeDocument, document);
|
||||
return document;
|
||||
}
|
||||
|
||||
private class StaticTagHelperFeature : ITagHelperFeature
|
||||
{
|
||||
public RazorEngine Engine { get; set; }
|
||||
|
||||
public List<TagHelperDescriptor> TagHelpers { get; set; }
|
||||
|
||||
public IReadOnlyList<TagHelperDescriptor> GetDescriptors()
|
||||
{
|
||||
return TagHelpers;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
// 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.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
public class ComponentMarkupEncodingPassTest
|
||||
{
|
||||
public ComponentMarkupEncodingPassTest()
|
||||
{
|
||||
Pass = new ComponentMarkupEncodingPass();
|
||||
ProjectEngine = (DefaultRazorProjectEngine)RazorProjectEngine.Create(
|
||||
RazorConfiguration.Default,
|
||||
RazorProjectFileSystem.Create(Environment.CurrentDirectory),
|
||||
b =>
|
||||
{
|
||||
if (b.Features.OfType<ComponentMarkupEncodingPass>().Any())
|
||||
{
|
||||
b.Features.Remove(b.Features.OfType<ComponentMarkupEncodingPass>().Single());
|
||||
}
|
||||
});
|
||||
Engine = ProjectEngine.Engine;
|
||||
|
||||
Pass.Engine = Engine;
|
||||
}
|
||||
|
||||
private DefaultRazorProjectEngine ProjectEngine { get; }
|
||||
|
||||
private RazorEngine Engine { get; }
|
||||
|
||||
private ComponentMarkupEncodingPass Pass { get; }
|
||||
|
||||
[Fact]
|
||||
public void Execute_StaticHtmlContent_RewrittenToBlock()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<div>
|
||||
|
||||
</div>");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(documentNode.FindDescendantNodes<HtmlContentIntermediateNode>());
|
||||
Assert.Single(documentNode.FindDescendantNodes<MarkupBlockIntermediateNode>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_MixedHtmlContent_NoNewLineorSpecialCharacters_DoesNotSetEncoded()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<div>The time is @DateTime.Now</div>");
|
||||
var expected = NormalizeContent("The time is ");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var node = documentNode.FindDescendantNodes<HtmlContentIntermediateNode>().Single();
|
||||
Assert.Equal(expected, GetHtmlContent(node));
|
||||
Assert.False(node.IsEncoded());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_MixedHtmlContent_NewLine_SetsEncoded()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<div>
|
||||
The time is @DateTime.Now</div>");
|
||||
var expected = NormalizeContent(@"
|
||||
The time is ");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var node = documentNode.FindDescendantNodes<HtmlContentIntermediateNode>().Single();
|
||||
Assert.Equal(expected, GetHtmlContent(node));
|
||||
Assert.True(node.IsEncoded());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_MixedHtmlContent_Ampersand_SetsEncoded()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<div>The time is & & @DateTime.Now</div>");
|
||||
var expected = NormalizeContent("The time is & & ");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var node = documentNode.FindDescendantNodes<HtmlContentIntermediateNode>().Single();
|
||||
Assert.Equal(expected, GetHtmlContent(node));
|
||||
Assert.True(node.IsEncoded());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_MixedHtmlContent_NonAsciiCharacter_SetsEncoded()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<div>ThĖ tĨme is @DateTime.Now</div>");
|
||||
var expected = NormalizeContent("ThĖ tĨme is ");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var node = documentNode.FindDescendantNodes<HtmlContentIntermediateNode>().Single();
|
||||
Assert.Equal(expected, GetHtmlContent(node));
|
||||
Assert.True(node.IsEncoded());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_MixedHtmlContent_HTMLEntity_DoesNotSetEncoded()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<div>The time = @DateTime.Now</div>");
|
||||
var expected = NormalizeContent("The time = ");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var node = documentNode.FindDescendantNodes<HtmlContentIntermediateNode>().Single();
|
||||
Assert.Equal(expected, GetHtmlContent(node));
|
||||
Assert.False(node.IsEncoded());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_MixedHtmlContent_MultipleHTMLEntities_DoesNotSetEncoded()
|
||||
{
|
||||
// Arrange
|
||||
var document = CreateDocument(@"
|
||||
<div>The time = =�x003D; @DateTime.Now</div>");
|
||||
var expected = NormalizeContent("The time =\u00A0== ");
|
||||
|
||||
var documentNode = Lower(document);
|
||||
|
||||
// Act
|
||||
Pass.Execute(document, documentNode);
|
||||
|
||||
// Assert
|
||||
var node = documentNode.FindDescendantNodes<HtmlContentIntermediateNode>().Single();
|
||||
Assert.Equal(expected, GetHtmlContent(node));
|
||||
Assert.False(node.IsEncoded());
|
||||
}
|
||||
|
||||
private string NormalizeContent(string content)
|
||||
{
|
||||
// Normalize newlines since we are testing lengths of things.
|
||||
content = content.Replace("\r", "");
|
||||
content = content.Replace("\n", "\r\n");
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
private RazorCodeDocument CreateDocument(string content)
|
||||
{
|
||||
// Normalize newlines since we are testing lengths of things.
|
||||
content = content.Replace("\r", "");
|
||||
content = content.Replace("\n", "\r\n");
|
||||
|
||||
var source = RazorSourceDocument.Create(content, "test.cshtml");
|
||||
return ProjectEngine.CreateCodeDocumentCore(source, FileKinds.Component);
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode Lower(RazorCodeDocument codeDocument)
|
||||
{
|
||||
for (var i = 0; i < Engine.Phases.Count; i++)
|
||||
{
|
||||
var phase = Engine.Phases[i];
|
||||
if (phase is IRazorCSharpLoweringPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
phase.Execute(codeDocument);
|
||||
}
|
||||
|
||||
var document = codeDocument.GetDocumentIntermediateNode();
|
||||
Engine.Features.OfType<ComponentDocumentClassifierPass>().Single().Execute(codeDocument, document);
|
||||
return document;
|
||||
}
|
||||
|
||||
private static string GetHtmlContent(HtmlContentIntermediateNode node)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
var htmlTokens = node.Children.OfType<IntermediateToken>().Where(t => t.IsHtml);
|
||||
foreach (var htmlToken in htmlTokens)
|
||||
{
|
||||
builder.Append(htmlToken.Content);
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
// 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.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal static class NodeAssert
|
||||
{
|
||||
public static HtmlAttributeIntermediateNode Attribute(IntermediateNode node, string attributeName, string attributeValue)
|
||||
{
|
||||
Assert.NotNull(node);
|
||||
|
||||
var attributeNode = Assert.IsType<HtmlAttributeIntermediateNode>(node);
|
||||
Assert.Equal(attributeName, attributeNode.AttributeName);
|
||||
|
||||
var attributeValueNode = Assert.IsType<HtmlAttributeValueIntermediateNode>(Assert.Single(attributeNode.Children));
|
||||
var actual = new StringBuilder();
|
||||
for (var i = 0; i < attributeValueNode.Children.Count; i++)
|
||||
{
|
||||
var token = Assert.IsType<IntermediateToken>(attributeValueNode.Children[i]);
|
||||
Assert.Equal(TokenKind.Html, token.Kind);
|
||||
actual.Append(token.Content);
|
||||
}
|
||||
|
||||
Assert.Equal(attributeValue, actual.ToString());
|
||||
|
||||
return attributeNode;
|
||||
}
|
||||
|
||||
public static HtmlAttributeIntermediateNode Attribute(IntermediateNodeCollection nodes, string attributeName, string attributeValue)
|
||||
{
|
||||
Assert.NotNull(nodes);
|
||||
return Attribute(Assert.Single(nodes), attributeName, attributeValue);
|
||||
}
|
||||
|
||||
public static HtmlContentIntermediateNode Content(IntermediateNode node, string content, bool trim = true)
|
||||
{
|
||||
Assert.NotNull(node);
|
||||
|
||||
var contentNode = Assert.IsType<HtmlContentIntermediateNode>(node);
|
||||
|
||||
var actual = new StringBuilder();
|
||||
for (var i = 0; i < contentNode.Children.Count; i++)
|
||||
{
|
||||
var token = Assert.IsType<IntermediateToken>(contentNode.Children[i]);
|
||||
Assert.Equal(TokenKind.Html, token.Kind);
|
||||
actual.Append(token.Content);
|
||||
}
|
||||
|
||||
Assert.Equal(content, trim ? actual.ToString().Trim() : actual.ToString());
|
||||
return contentNode;
|
||||
}
|
||||
|
||||
public static HtmlContentIntermediateNode Content(IntermediateNodeCollection nodes, string content, bool trim = true)
|
||||
{
|
||||
Assert.NotNull(nodes);
|
||||
return Content(Assert.Single(nodes), content, trim);
|
||||
}
|
||||
|
||||
public static HtmlAttributeIntermediateNode CSharpAttribute(IntermediateNode node, string attributeName, string attributeValue)
|
||||
{
|
||||
Assert.NotNull(node);
|
||||
|
||||
var attributeNode = Assert.IsType<HtmlAttributeIntermediateNode>(node);
|
||||
Assert.Equal(attributeName, attributeNode.AttributeName);
|
||||
|
||||
var attributeValueNode = Assert.IsType<CSharpExpressionAttributeValueIntermediateNode>(Assert.Single(attributeNode.Children));
|
||||
var actual = new StringBuilder();
|
||||
for (var i = 0; i < attributeValueNode.Children.Count; i++)
|
||||
{
|
||||
var token = Assert.IsType<IntermediateToken>(attributeValueNode.Children[i]);
|
||||
Assert.Equal(TokenKind.CSharp, token.Kind);
|
||||
actual.Append(token.Content);
|
||||
}
|
||||
|
||||
Assert.Equal(attributeValue, actual.ToString());
|
||||
|
||||
return attributeNode;
|
||||
}
|
||||
|
||||
public static HtmlAttributeIntermediateNode CSharpAttribute(IntermediateNodeCollection nodes, string attributeName, string attributeValue)
|
||||
{
|
||||
Assert.NotNull(nodes);
|
||||
return Attribute(Assert.Single(nodes), attributeName, attributeValue);
|
||||
}
|
||||
|
||||
public static MarkupElementIntermediateNode Element(IntermediateNode node, string tagName)
|
||||
{
|
||||
Assert.NotNull(node);
|
||||
|
||||
var elementNode = Assert.IsType<MarkupElementIntermediateNode>(node);
|
||||
Assert.Equal(tagName, elementNode.TagName);
|
||||
return elementNode;
|
||||
}
|
||||
|
||||
public static MarkupElementIntermediateNode Element(IntermediateNodeCollection nodes, string tagName)
|
||||
{
|
||||
Assert.NotNull(nodes);
|
||||
return Element(Assert.Single(nodes), tagName);
|
||||
}
|
||||
|
||||
public static HtmlContentIntermediateNode Whitespace(IntermediateNode node)
|
||||
{
|
||||
Assert.NotNull(node);
|
||||
|
||||
var contentNode = Assert.IsType<HtmlContentIntermediateNode>(node);
|
||||
for (var i = 0; i < contentNode.Children.Count; i++)
|
||||
{
|
||||
var token = Assert.IsType<IntermediateToken>(contentNode.Children[i]);
|
||||
Assert.Equal(TokenKind.Html, token.Kind);
|
||||
Assert.True(string.IsNullOrWhiteSpace(token.Content));
|
||||
}
|
||||
|
||||
return contentNode;
|
||||
}
|
||||
|
||||
public static HtmlContentIntermediateNode Whitespace(IntermediateNodeCollection nodes)
|
||||
{
|
||||
Assert.NotNull(nodes);
|
||||
return Whitespace(Assert.Single(nodes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultAllowedChildTagDescriptorBuilderTest
|
||||
{
|
||||
[Fact]
|
||||
public void Build_DisplayNameIsName()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultAllowedChildTagDescriptorBuilder(null);
|
||||
builder.Name = "foo";
|
||||
|
||||
// Act
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.Equal("foo", descriptor.DisplayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultBoundAttributeDescriptorBuilderTest
|
||||
{
|
||||
[Fact]
|
||||
public void DisplayName_SetsDescriptorsDisplayName()
|
||||
{
|
||||
// Arrange
|
||||
var expectedDisplayName = "ExpectedDisplayName";
|
||||
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder.DisplayName(expectedDisplayName);
|
||||
|
||||
// Act
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDisplayName, descriptor.DisplayName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisplayName_DefaultsToPropertyLookingDisplayName()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("TestTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.TypeName(typeof(int).FullName)
|
||||
.PropertyName("SomeProperty");
|
||||
|
||||
// Act
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.Equal("int TestTagHelper.SomeProperty", descriptor.DisplayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
using static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeAssert;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
// We're purposely lean on tests here because the functionality is well covered by
|
||||
// integration tests, and is mostly implemented by the base class.
|
||||
public class DefaultDocumentClassifierPassTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Latest;
|
||||
|
||||
[Fact]
|
||||
public void Execute_IgnoresDocumentsWithDocumentKind()
|
||||
{
|
||||
// Arrange
|
||||
var documentNode = new DocumentIntermediateNode()
|
||||
{
|
||||
DocumentKind = "ignore",
|
||||
Options = RazorCodeGenerationOptions.CreateDefault(),
|
||||
};
|
||||
|
||||
var pass = new DefaultDocumentClassifierPass();
|
||||
pass.Engine = CreateProjectEngine().Engine;
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), documentNode);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("ignore", documentNode.DocumentKind);
|
||||
NoChildren(documentNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_CreatesClassStructure()
|
||||
{
|
||||
// Arrange
|
||||
var documentNode = new DocumentIntermediateNode()
|
||||
{
|
||||
Options = RazorCodeGenerationOptions.CreateDefault(),
|
||||
};
|
||||
|
||||
var pass = new DefaultDocumentClassifierPass();
|
||||
pass.Engine = CreateProjectEngine().Engine;
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), documentNode);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("default", documentNode.DocumentKind);
|
||||
|
||||
var @namespace = SingleChild<NamespaceDeclarationIntermediateNode>(documentNode);
|
||||
var @class = SingleChild<ClassDeclarationIntermediateNode>(@namespace);
|
||||
var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
|
||||
NoChildren(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class ItemCollectionTest
|
||||
{
|
||||
[Fact]
|
||||
public void Get_MissingValueReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var items = new ItemCollection();
|
||||
|
||||
// Act
|
||||
var value = items["foo"];
|
||||
|
||||
// Assert
|
||||
Assert.Null(value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAndSet_ReturnsValue()
|
||||
{
|
||||
// Arrange
|
||||
var items = new ItemCollection();
|
||||
|
||||
var expected = "bar";
|
||||
items["foo"] = expected;
|
||||
|
||||
// Act
|
||||
var value = items["foo"];
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Set_CanSetValueToNull()
|
||||
{
|
||||
// Arrange
|
||||
var items = new ItemCollection();
|
||||
|
||||
items["foo"] = "bar";
|
||||
|
||||
// Act
|
||||
items["foo"] = null;
|
||||
|
||||
// Assert
|
||||
Assert.Null(items["foo"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorCSharpLoweringPhaseTest
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_ThrowsForMissingDependency_IRDocument()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorCSharpLoweringPhase();
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source));
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
() => phase.Execute(codeDocument),
|
||||
$"The '{nameof(DefaultRazorCSharpLoweringPhase)}' phase requires a '{nameof(DocumentIntermediateNode)}' " +
|
||||
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ThrowsForMissingDependency_CodeTarget()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorCSharpLoweringPhase();
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source));
|
||||
|
||||
var irDocument = new DocumentIntermediateNode()
|
||||
{
|
||||
DocumentKind = "test",
|
||||
};
|
||||
codeDocument.SetDocumentIntermediateNode(irDocument);
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
() => phase.Execute(codeDocument),
|
||||
$"The document of kind 'test' does not have a '{nameof(CodeTarget)}'. " +
|
||||
$"The document classifier must set a value for '{nameof(DocumentIntermediateNode.Target)}'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_CollatesIRDocumentDiagnosticsFromSourceDocument()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorCSharpLoweringPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||
var codeDocument = TestRazorCodeDocument.Create("<p class=@(");
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
var irDocument = new DocumentIntermediateNode()
|
||||
{
|
||||
DocumentKind = "test",
|
||||
Target = CodeTarget.CreateDefault(codeDocument, options),
|
||||
Options = options,
|
||||
};
|
||||
var expectedDiagnostic = RazorDiagnostic.Create(
|
||||
new RazorDiagnosticDescriptor("1234", () => "I am an error.", RazorDiagnosticSeverity.Error),
|
||||
new SourceSpan("SomeFile.cshtml", 11, 0, 11, 1));
|
||||
irDocument.Diagnostics.Add(expectedDiagnostic);
|
||||
codeDocument.SetDocumentIntermediateNode(irDocument);
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var csharpDocument = codeDocument.GetCSharpDocument();
|
||||
var diagnostic = Assert.Single(csharpDocument.Diagnostics);
|
||||
Assert.Same(expectedDiagnostic, diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorCodeDocumentTest
|
||||
{
|
||||
[Fact]
|
||||
public void Ctor()
|
||||
{
|
||||
// Arrange
|
||||
var source = TestRazorSourceDocument.Create();
|
||||
|
||||
var imports = new RazorSourceDocument[]
|
||||
{
|
||||
TestRazorSourceDocument.Create(),
|
||||
};
|
||||
|
||||
// Act
|
||||
var code = new DefaultRazorCodeDocument(source, imports);
|
||||
|
||||
// Assert
|
||||
Assert.Same(source, code.Source);
|
||||
Assert.NotNull(code.Items);
|
||||
|
||||
Assert.NotSame(imports, code.Imports);
|
||||
Assert.Collection(imports, d => Assert.Same(imports[0], d));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ctor_AllowsNullForImports()
|
||||
{
|
||||
// Arrange
|
||||
var source = TestRazorSourceDocument.Create();
|
||||
|
||||
// Act
|
||||
var code = new DefaultRazorCodeDocument(source, imports: null);
|
||||
|
||||
// Assert
|
||||
Assert.Same(source, code.Source);
|
||||
Assert.NotNull(code.Items);
|
||||
Assert.Empty(code.Imports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
// 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.Globalization;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorDiagnosticTest
|
||||
{
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_Ctor()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "error", RazorDiagnosticSeverity.Error);
|
||||
var span = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
|
||||
// Act
|
||||
var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("RZ0000", diagnostic.Id);
|
||||
Assert.Equal(RazorDiagnosticSeverity.Error, diagnostic.Severity);
|
||||
Assert.Equal(span, diagnostic.Span);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_GetMessage()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "error", RazorDiagnosticSeverity.Error);
|
||||
var span = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
|
||||
var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
|
||||
|
||||
// Act
|
||||
var result = diagnostic.GetMessage();
|
||||
|
||||
// Assert
|
||||
Assert.Equal("error", result);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_GetMessage_WithArgs()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
|
||||
var span = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
|
||||
var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new[] { "error" });
|
||||
|
||||
// Act
|
||||
var result = diagnostic.GetMessage();
|
||||
|
||||
// Assert
|
||||
Assert.Equal("this is an error", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_GetMessage_WithArgs_FormatProvider()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
|
||||
var span = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
|
||||
var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new object[] { 1.3m });
|
||||
|
||||
// Act
|
||||
var result = diagnostic.GetMessage(new CultureInfo("fr-FR"));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("this is an 1,3", result);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_ToString()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an error", RazorDiagnosticSeverity.Error);
|
||||
var span = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
|
||||
var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
|
||||
|
||||
// Act
|
||||
var result = diagnostic.ToString();
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test.cs(2,9): Error RZ0000: this is an error", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_ToString_FormatProvider()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
|
||||
var span = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
|
||||
var diagnostic = new DefaultRazorDiagnostic(descriptor, span, new object[] { 1.3m });
|
||||
|
||||
// Act
|
||||
var result = ((IFormattable)diagnostic).ToString("ignored", new CultureInfo("fr-FR"));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test.cs(2,9): Error RZ0000: this is an 1,3", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_Equals()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
|
||||
var span = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
|
||||
var diagnostic1 = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
|
||||
var diagnostic2 = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
|
||||
|
||||
// Act
|
||||
var result = diagnostic1.Equals(diagnostic2);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_NotEquals_DifferentLocation()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
|
||||
var span1 = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
var span2 = new SourceSpan("test.cs", 15, 1, 8, 3);
|
||||
|
||||
var diagnostic1 = new DefaultRazorDiagnostic(descriptor, span1, new object[0]);
|
||||
var diagnostic2 = new DefaultRazorDiagnostic(descriptor, span2, new object[0]);
|
||||
|
||||
// Act
|
||||
var result = diagnostic1.Equals(diagnostic2);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_NotEquals_DifferentId()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor1 = new RazorDiagnosticDescriptor("RZ0001", () => "this is an {0}", RazorDiagnosticSeverity.Error);
|
||||
var descriptor2 = new RazorDiagnosticDescriptor("RZ0002", () => "this is an {0}", RazorDiagnosticSeverity.Error);
|
||||
var span = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
|
||||
var diagnostic1 = new DefaultRazorDiagnostic(descriptor1, span, new object[0]);
|
||||
var diagnostic2 = new DefaultRazorDiagnostic(descriptor2, span, new object[0]);
|
||||
|
||||
// Act
|
||||
var result = diagnostic1.Equals(diagnostic2);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_HashCodesEqual()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
|
||||
var span = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
|
||||
var diagnostic1 = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
|
||||
var diagnostic2 = new DefaultRazorDiagnostic(descriptor, span, new object[0]);
|
||||
|
||||
// Act
|
||||
var result = diagnostic1.GetHashCode() == diagnostic2.GetHashCode();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_HashCodesNotEqual_DifferentLocation()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new RazorDiagnosticDescriptor("RZ0000", () => "this is an {0}", RazorDiagnosticSeverity.Error);
|
||||
var span1 = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
var span2 = new SourceSpan("test.cs", 15, 1, 8, 3);
|
||||
|
||||
var diagnostic1 = new DefaultRazorDiagnostic(descriptor, span1, new object[0]);
|
||||
var diagnostic2 = new DefaultRazorDiagnostic(descriptor, span2, new object[0]);
|
||||
|
||||
// Act
|
||||
var result = diagnostic1.GetHashCode() == diagnostic2.GetHashCode();
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorDiagnostic_HashCodesNotEqual_DifferentId()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor1 = new RazorDiagnosticDescriptor("RZ0001", () => "this is an {0}", RazorDiagnosticSeverity.Error);
|
||||
var descriptor2 = new RazorDiagnosticDescriptor("RZ0002", () => "this is an {0}", RazorDiagnosticSeverity.Error);
|
||||
var span = new SourceSpan("test.cs", 15, 1, 8, 5);
|
||||
|
||||
var diagnostic1 = new DefaultRazorDiagnostic(descriptor1, span, new object[0]);
|
||||
var diagnostic2 = new DefaultRazorDiagnostic(descriptor2, span, new object[0]);
|
||||
|
||||
// Act
|
||||
var result = diagnostic1.GetHashCode() == diagnostic2.GetHashCode();
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorDirectiveClassifierPhaseTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnInitialized_OrdersPassesInAscendingOrder()
|
||||
{
|
||||
// Arrange & Act
|
||||
var phase = new DefaultRazorDirectiveClassifierPhase();
|
||||
|
||||
var first = Mock.Of<IRazorDirectiveClassifierPass>(p => p.Order == 15);
|
||||
var second = Mock.Of<IRazorDirectiveClassifierPass>(p => p.Order == 17);
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
|
||||
b.Features.Add(second);
|
||||
b.Features.Add(first);
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
phase.Passes,
|
||||
p => Assert.Same(first, p),
|
||||
p => Assert.Same(second, p));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ThrowsForMissingDependency()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorDirectiveClassifierPhase();
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
() => phase.Execute(codeDocument),
|
||||
$"The '{nameof(DefaultRazorDirectiveClassifierPhase)}' phase requires a '{nameof(DocumentIntermediateNode)}' " +
|
||||
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ExecutesPhasesInOrder()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// We're going to set up mocks to simulate a sequence of passes. We don't care about
|
||||
// what's in the nodes, we're just going to look at the identity via strict mocks.
|
||||
var originalNode = new DocumentIntermediateNode();
|
||||
var firstPassNode = new DocumentIntermediateNode();
|
||||
var secondPassNode = new DocumentIntermediateNode();
|
||||
codeDocument.SetDocumentIntermediateNode(originalNode);
|
||||
|
||||
var firstPass = new Mock<IRazorDirectiveClassifierPass>(MockBehavior.Strict);
|
||||
firstPass.SetupGet(m => m.Order).Returns(0);
|
||||
firstPass.SetupProperty(m => m.Engine);
|
||||
firstPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||
{
|
||||
originalNode.Children.Add(firstPassNode);
|
||||
});
|
||||
|
||||
var secondPass = new Mock<IRazorDirectiveClassifierPass>(MockBehavior.Strict);
|
||||
secondPass.SetupGet(m => m.Order).Returns(1);
|
||||
secondPass.SetupProperty(m => m.Engine);
|
||||
secondPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||
{
|
||||
// Works only when the first pass has run before this.
|
||||
originalNode.Children[0].Children.Add(secondPassNode);
|
||||
});
|
||||
|
||||
var phase = new DefaultRazorDirectiveClassifierPhase();
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
|
||||
b.Features.Add(firstPass.Object);
|
||||
b.Features.Add(secondPass.Object);
|
||||
});
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Same(secondPassNode, codeDocument.GetDocumentIntermediateNode().Children[0].Children[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorDocumentClassifierPhaseTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnInitialized_OrdersPassesInAscendingOrder()
|
||||
{
|
||||
// Arrange & Act
|
||||
var phase = new DefaultRazorDocumentClassifierPhase();
|
||||
|
||||
var first = Mock.Of<IRazorDocumentClassifierPass>(p => p.Order == 15);
|
||||
var second = Mock.Of<IRazorDocumentClassifierPass>(p => p.Order == 17);
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
|
||||
b.Features.Add(second);
|
||||
b.Features.Add(first);
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
phase.Passes,
|
||||
p => Assert.Same(first, p),
|
||||
p => Assert.Same(second, p));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ThrowsForMissingDependency()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorDocumentClassifierPhase();
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
() => phase.Execute(codeDocument),
|
||||
$"The '{nameof(DefaultRazorDocumentClassifierPhase)}' phase requires a '{nameof(DocumentIntermediateNode)}' " +
|
||||
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ExecutesPhasesInOrder()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// We're going to set up mocks to simulate a sequence of passes. We don't care about
|
||||
// what's in the nodes, we're just going to look at the identity via strict mocks.
|
||||
var originalNode = new DocumentIntermediateNode();
|
||||
var firstPassNode = new DocumentIntermediateNode();
|
||||
var secondPassNode = new DocumentIntermediateNode();
|
||||
codeDocument.SetDocumentIntermediateNode(originalNode);
|
||||
|
||||
var firstPass = new Mock<IRazorDocumentClassifierPass>(MockBehavior.Strict);
|
||||
firstPass.SetupGet(m => m.Order).Returns(0);
|
||||
firstPass.SetupProperty(m => m.Engine);
|
||||
firstPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||
{
|
||||
originalNode.Children.Add(firstPassNode);
|
||||
});
|
||||
|
||||
var secondPass = new Mock<IRazorDocumentClassifierPass>(MockBehavior.Strict);
|
||||
secondPass.SetupGet(m => m.Order).Returns(1);
|
||||
secondPass.SetupProperty(m => m.Engine);
|
||||
secondPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||
{
|
||||
// Works only when the first pass has run before this.
|
||||
originalNode.Children[0].Children.Add(secondPassNode);
|
||||
});
|
||||
|
||||
var phase = new DefaultRazorDocumentClassifierPhase();
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
|
||||
b.Features.Add(firstPass.Object);
|
||||
b.Features.Add(secondPass.Object);
|
||||
});
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Same(secondPassNode, codeDocument.GetDocumentIntermediateNode().Children[0].Children[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// 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.Linq;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorEngineBuilderTest
|
||||
{
|
||||
[Fact]
|
||||
public void Build_AddsFeaturesAndPhases()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorEngineBuilder(designTime: false);
|
||||
|
||||
builder.Features.Add(Mock.Of<IRazorEngineFeature>());
|
||||
builder.Features.Add(Mock.Of<IRazorEngineFeature>());
|
||||
|
||||
builder.Phases.Add(Mock.Of<IRazorEnginePhase>());
|
||||
builder.Phases.Add(Mock.Of<IRazorEnginePhase>());
|
||||
|
||||
var features = builder.Features.ToArray();
|
||||
var phases = builder.Phases.ToArray();
|
||||
|
||||
// Act
|
||||
var engine = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
engine.Features,
|
||||
f => Assert.Same(features[0], f),
|
||||
f => Assert.Same(features[1], f));
|
||||
|
||||
Assert.Collection(
|
||||
engine.Phases,
|
||||
p => Assert.Same(phases[0], p),
|
||||
p => Assert.Same(phases[1], p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// 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 Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorEngineTest
|
||||
{
|
||||
[Fact]
|
||||
public void Ctor_InitializesPhasesAndFeatures()
|
||||
{
|
||||
// Arrange
|
||||
var features = new IRazorEngineFeature[]
|
||||
{
|
||||
Mock.Of<IRazorEngineFeature>(),
|
||||
Mock.Of<IRazorEngineFeature>(),
|
||||
};
|
||||
|
||||
var phases = new IRazorEnginePhase[]
|
||||
{
|
||||
Mock.Of<IRazorEnginePhase>(),
|
||||
Mock.Of<IRazorEnginePhase>(),
|
||||
};
|
||||
|
||||
// Act
|
||||
var engine = new DefaultRazorEngine(features, phases);
|
||||
|
||||
// Assert
|
||||
for (var i = 0; i < features.Length; i++)
|
||||
{
|
||||
Assert.Same(engine, features[i].Engine);
|
||||
}
|
||||
|
||||
for (var i = 0; i < phases.Length; i++)
|
||||
{
|
||||
Assert.Same(engine, phases[i].Engine);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_CallsAllPhases()
|
||||
{
|
||||
// Arrange
|
||||
var features = new IRazorEngineFeature[]
|
||||
{
|
||||
Mock.Of<IRazorEngineFeature>(),
|
||||
Mock.Of<IRazorEngineFeature>(),
|
||||
};
|
||||
|
||||
var phases = new IRazorEnginePhase[]
|
||||
{
|
||||
Mock.Of<IRazorEnginePhase>(),
|
||||
Mock.Of<IRazorEnginePhase>(),
|
||||
};
|
||||
|
||||
var engine = new DefaultRazorEngine(features, phases);
|
||||
var document = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act
|
||||
engine.Process(document);
|
||||
|
||||
// Assert
|
||||
for (var i = 0; i < phases.Length; i++)
|
||||
{
|
||||
var mock = Mock.Get(phases[i]);
|
||||
mock.Verify(p => p.Execute(document), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,541 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeAssert;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorIntermediateNodeLoweringPhaseIntegrationTest
|
||||
{
|
||||
[Fact]
|
||||
public void Lower_SetsOptions_Defaults()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(documentNode.Options);
|
||||
Assert.False(documentNode.Options.DesignTime);
|
||||
Assert.Equal(4, documentNode.Options.IndentSize);
|
||||
Assert.False(documentNode.Options.IndentWithTabs);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_SetsOptions_RunsConfigureCallbacks()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
var callback = new Mock<IConfigureRazorCodeGenerationOptionsFeature>();
|
||||
callback
|
||||
.Setup(c => c.Configure(It.IsAny<RazorCodeGenerationOptionsBuilder>()))
|
||||
.Callback<RazorCodeGenerationOptionsBuilder>(o =>
|
||||
{
|
||||
o.IndentSize = 17;
|
||||
o.IndentWithTabs = true;
|
||||
o.SuppressChecksum = true;
|
||||
});
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(
|
||||
codeDocument,
|
||||
builder: b =>
|
||||
{
|
||||
b.Features.Add(callback.Object);
|
||||
},
|
||||
designTime: true);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(documentNode.Options);
|
||||
Assert.True(documentNode.Options.DesignTime);
|
||||
Assert.Equal(17, documentNode.Options.IndentSize);
|
||||
Assert.True(documentNode.Options.IndentWithTabs);
|
||||
Assert.True(documentNode.Options.SuppressChecksum);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_HelloWorld()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.Create("Hello, World!");
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
Children(documentNode,
|
||||
n => Html("Hello, World!", n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_HtmlWithDataDashAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.Create(@"
|
||||
<html>
|
||||
<body>
|
||||
<span data-val=""@Hello"" />
|
||||
</body>
|
||||
</html>");
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
Children(documentNode,
|
||||
n => Html(
|
||||
@"
|
||||
<html>
|
||||
<body>
|
||||
<span data-val=""", n),
|
||||
n => CSharpExpression("Hello", n),
|
||||
n => Html(@""" />
|
||||
</body>
|
||||
</html>", n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_HtmlWithConditionalAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.Create(@"
|
||||
<html>
|
||||
<body>
|
||||
<span val=""@Hello World"" />
|
||||
</body>
|
||||
</html>");
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
Children(documentNode,
|
||||
n => Html(
|
||||
@"
|
||||
<html>
|
||||
<body>
|
||||
<span", n),
|
||||
|
||||
n => ConditionalAttribute(
|
||||
prefix: " val=\"",
|
||||
name: "val",
|
||||
suffix: "\"",
|
||||
node: n,
|
||||
valueValidators: new Action<IntermediateNode>[]
|
||||
{
|
||||
value => CSharpExpressionAttributeValue(string.Empty, "Hello", value),
|
||||
value => LiteralAttributeValue(" ", "World", value)
|
||||
}),
|
||||
n => Html(@" />
|
||||
</body>
|
||||
</html>", n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_WithFunctions()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.Create(@"@functions { public int Foo { get; set; }}");
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
Children(documentNode,
|
||||
n => Directive(
|
||||
"functions",
|
||||
n,
|
||||
c => Assert.IsType<CSharpCodeIntermediateNode>(c)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_WithUsing()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.Create(@"@using System");
|
||||
var expectedSourceLocation = new SourceSpan(codeDocument.Source.FilePath, 1, 0, 1, 12);
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
Children(documentNode,
|
||||
n =>
|
||||
{
|
||||
Using("System", n);
|
||||
Assert.Equal(expectedSourceLocation, n.Source);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_TagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.Create(@"@addTagHelper *, TestAssembly
|
||||
<span val=""@Hello World""></span>");
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "span",
|
||||
typeName: "SpanTagHelper",
|
||||
assemblyName: "TestAssembly")
|
||||
};
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument, tagHelpers: tagHelpers);
|
||||
|
||||
// Assert
|
||||
Children(documentNode,
|
||||
n => Directive(
|
||||
SyntaxConstants.CSharp.AddTagHelperKeyword,
|
||||
n,
|
||||
v => DirectiveToken(DirectiveTokenKind.String, "*, TestAssembly", v)),
|
||||
n => TagHelper(
|
||||
"span",
|
||||
TagMode.StartTagAndEndTag,
|
||||
tagHelpers,
|
||||
n,
|
||||
c => Assert.IsType<TagHelperBodyIntermediateNode>(c),
|
||||
c => TagHelperHtmlAttribute(
|
||||
"val",
|
||||
AttributeStructure.DoubleQuotes,
|
||||
c,
|
||||
v => CSharpExpressionAttributeValue(string.Empty, "Hello", v),
|
||||
v => LiteralAttributeValue(" ", "World", v))));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_TagHelpers_WithPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.Create(@"@addTagHelper *, TestAssembly
|
||||
@tagHelperPrefix cool:
|
||||
<cool:span val=""@Hello World""></cool:span>");
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "span",
|
||||
typeName: "SpanTagHelper",
|
||||
assemblyName: "TestAssembly")
|
||||
};
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument, tagHelpers: tagHelpers);
|
||||
|
||||
// Assert
|
||||
Children(documentNode,
|
||||
n => Directive(
|
||||
SyntaxConstants.CSharp.AddTagHelperKeyword,
|
||||
n,
|
||||
v => DirectiveToken(DirectiveTokenKind.String, "*, TestAssembly", v)),
|
||||
n => Directive(
|
||||
SyntaxConstants.CSharp.TagHelperPrefixKeyword,
|
||||
n,
|
||||
v => DirectiveToken(DirectiveTokenKind.String, "cool:", v)),
|
||||
n => TagHelper(
|
||||
"span", // Note: this is span not cool:span
|
||||
TagMode.StartTagAndEndTag,
|
||||
tagHelpers,
|
||||
n,
|
||||
c => Assert.IsType<TagHelperBodyIntermediateNode>(c),
|
||||
c => TagHelperHtmlAttribute(
|
||||
"val",
|
||||
AttributeStructure.DoubleQuotes,
|
||||
c,
|
||||
v => CSharpExpressionAttributeValue(string.Empty, "Hello", v),
|
||||
v => LiteralAttributeValue(" ", "World", v))));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_TagHelper_InSection()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.Create(@"@addTagHelper *, TestAssembly
|
||||
@section test {
|
||||
<span val=""@Hello World""></span>
|
||||
}");
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "span",
|
||||
typeName: "SpanTagHelper",
|
||||
assemblyName: "TestAssembly")
|
||||
};
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument, tagHelpers: tagHelpers);
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
documentNode,
|
||||
n => Directive(
|
||||
SyntaxConstants.CSharp.AddTagHelperKeyword,
|
||||
n,
|
||||
v => DirectiveToken(DirectiveTokenKind.String, "*, TestAssembly", v)),
|
||||
n => Directive(
|
||||
"section",
|
||||
n,
|
||||
c1 => DirectiveToken(DirectiveTokenKind.Member, "test", c1),
|
||||
c1 => Html(Environment.NewLine, c1),
|
||||
c1 => TagHelper(
|
||||
"span",
|
||||
TagMode.StartTagAndEndTag,
|
||||
tagHelpers,
|
||||
c1,
|
||||
c2 => Assert.IsType<TagHelperBodyIntermediateNode>(c2),
|
||||
c2 => TagHelperHtmlAttribute(
|
||||
"val",
|
||||
AttributeStructure.DoubleQuotes,
|
||||
c2,
|
||||
v => CSharpExpressionAttributeValue(string.Empty, "Hello", v),
|
||||
v => LiteralAttributeValue(" ", "World", v))),
|
||||
c1 => Html(Environment.NewLine, c1)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_TagHelpersWithBoundAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.Create(@"@addTagHelper *, TestAssembly
|
||||
<input bound='foo' />");
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("bound")
|
||||
.PropertyName("FooProp")
|
||||
.TypeName("System.String"),
|
||||
})
|
||||
};
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument, tagHelpers: tagHelpers);
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
documentNode,
|
||||
n => Directive(
|
||||
SyntaxConstants.CSharp.AddTagHelperKeyword,
|
||||
n,
|
||||
v => DirectiveToken(DirectiveTokenKind.String, "*, TestAssembly", v)),
|
||||
n => TagHelper(
|
||||
"input",
|
||||
TagMode.SelfClosing,
|
||||
tagHelpers,
|
||||
n,
|
||||
c => Assert.IsType<TagHelperBodyIntermediateNode>(c),
|
||||
c => SetTagHelperProperty(
|
||||
"bound",
|
||||
"FooProp",
|
||||
AttributeStructure.SingleQuotes,
|
||||
c,
|
||||
v => Html("foo", v))));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_WithImports_Using()
|
||||
{
|
||||
// Arrange
|
||||
var source = TestRazorSourceDocument.Create(@"@using System.Threading.Tasks
|
||||
<p>Hi!</p>");
|
||||
var imports = new[]
|
||||
{
|
||||
TestRazorSourceDocument.Create("@using System.Globalization"),
|
||||
TestRazorSourceDocument.Create("@using System.Text"),
|
||||
};
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.Create(source, imports);
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
documentNode,
|
||||
n => Using("System.Globalization", n),
|
||||
n => Using("System.Text", n),
|
||||
n => Using("System.Threading.Tasks", n),
|
||||
n => Html("<p>Hi!</p>", n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_WithImports_AllowsIdenticalNamespacesInPrimaryDocument()
|
||||
{
|
||||
// Arrange
|
||||
var source = TestRazorSourceDocument.Create(@"@using System.Threading.Tasks
|
||||
@using System.Threading.Tasks");
|
||||
var imports = new[]
|
||||
{
|
||||
TestRazorSourceDocument.Create("@using System.Threading.Tasks"),
|
||||
};
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.Create(source, imports);
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
documentNode,
|
||||
n => Using("System.Threading.Tasks", n),
|
||||
n => Using("System.Threading.Tasks", n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_WithMultipleImports_SingleLineFileScopedSinglyOccurring()
|
||||
{
|
||||
// Arrange
|
||||
var source = TestRazorSourceDocument.Create("<p>Hi!</p>");
|
||||
var imports = new[]
|
||||
{
|
||||
TestRazorSourceDocument.Create("@test value1"),
|
||||
TestRazorSourceDocument.Create("@test value2"),
|
||||
};
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.Create(source, imports);
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument, b =>
|
||||
{
|
||||
b.AddDirective(DirectiveDescriptor.CreateDirective(
|
||||
"test",
|
||||
DirectiveKind.SingleLine,
|
||||
builder =>
|
||||
{
|
||||
builder.AddMemberToken();
|
||||
builder.Usage = DirectiveUsage.FileScopedSinglyOccurring;
|
||||
}));
|
||||
});
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
documentNode,
|
||||
n => Directive("test", n, c => DirectiveToken(DirectiveTokenKind.Member, "value2", c)),
|
||||
n => Html("<p>Hi!</p>", n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lower_WithImports_IgnoresBlockDirective()
|
||||
{
|
||||
// Arrange
|
||||
var source = TestRazorSourceDocument.Create("<p>Hi!</p>");
|
||||
var imports = new[]
|
||||
{
|
||||
TestRazorSourceDocument.Create("@block token { }"),
|
||||
};
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.Create(source, imports);
|
||||
|
||||
// Act
|
||||
var documentNode = Lower(codeDocument, b =>
|
||||
{
|
||||
b.AddDirective(DirectiveDescriptor.CreateDirective("block", DirectiveKind.RazorBlock, d => d.AddMemberToken()));
|
||||
});
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
documentNode,
|
||||
n => Html("<p>Hi!</p>", n));
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode Lower(
|
||||
RazorCodeDocument codeDocument,
|
||||
Action<RazorProjectEngineBuilder> builder = null,
|
||||
IEnumerable<TagHelperDescriptor> tagHelpers = null,
|
||||
bool designTime = false)
|
||||
{
|
||||
tagHelpers = tagHelpers ?? new TagHelperDescriptor[0];
|
||||
|
||||
Action<RazorProjectEngineBuilder> configureEngine = b =>
|
||||
{
|
||||
builder?.Invoke(b);
|
||||
|
||||
SectionDirective.Register(b);
|
||||
b.AddTagHelpers(tagHelpers);
|
||||
|
||||
b.Features.Add(new DesignTimeOptionsFeature(designTime));
|
||||
};
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(configureEngine);
|
||||
|
||||
for (var i = 0; i < projectEngine.Phases.Count; i++)
|
||||
{
|
||||
var phase = projectEngine.Phases[i];
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
if (phase is IRazorIntermediateNodeLoweringPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
Assert.NotNull(documentNode);
|
||||
|
||||
return documentNode;
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor CreateTagHelperDescriptor(
|
||||
string tagName,
|
||||
string typeName,
|
||||
string assemblyName,
|
||||
IEnumerable<Action<BoundAttributeDescriptorBuilder>> attributes = null)
|
||||
{
|
||||
var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||
builder.TypeName(typeName);
|
||||
|
||||
if (attributes != null)
|
||||
{
|
||||
foreach (var attributeBuilder in attributes)
|
||||
{
|
||||
builder.BoundAttributeDescriptor(attributeBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
builder.TagMatchingRuleDescriptor(ruleBuilder => ruleBuilder.RequireTagName(tagName));
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private class DesignTimeOptionsFeature : IConfigureRazorParserOptionsFeature, IConfigureRazorCodeGenerationOptionsFeature
|
||||
{
|
||||
private bool _designTime;
|
||||
|
||||
public DesignTimeOptionsFeature(bool designTime)
|
||||
{
|
||||
_designTime = designTime;
|
||||
}
|
||||
|
||||
public int Order { get; }
|
||||
|
||||
public RazorEngine Engine { get; set; }
|
||||
|
||||
public void Configure(RazorParserOptionsBuilder options)
|
||||
{
|
||||
options.SetDesignTime(_designTime);
|
||||
}
|
||||
|
||||
public void Configure(RazorCodeGenerationOptionsBuilder options)
|
||||
{
|
||||
options.SetDesignTime(_designTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,298 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorIntermediateNodeLoweringPhaseTest
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_AutomaticallyImportsSingleLineSinglyOccurringDirective()
|
||||
{
|
||||
// Arrange
|
||||
var directive = DirectiveDescriptor.CreateSingleLineDirective(
|
||||
"custom",
|
||||
builder =>
|
||||
{
|
||||
builder.AddStringToken();
|
||||
builder.Usage = DirectiveUsage.FileScopedSinglyOccurring;
|
||||
});
|
||||
var phase = new DefaultRazorIntermediateNodeLoweringPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
b.AddDirective(directive);
|
||||
});
|
||||
var options = RazorParserOptions.Create(builder => builder.Directives.Add(directive));
|
||||
var importSource = TestRazorSourceDocument.Create("@custom \"hello\"", filePath: "import.cshtml");
|
||||
var codeDocument = TestRazorCodeDocument.Create("<p>NonDirective</p>");
|
||||
codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source, options));
|
||||
codeDocument.SetImportSyntaxTrees(new[] { RazorSyntaxTree.Parse(importSource, options) });
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
var customDirectives = documentNode.FindDirectiveReferences(directive);
|
||||
var customDirective = (DirectiveIntermediateNode)Assert.Single(customDirectives).Node;
|
||||
var stringToken = Assert.Single(customDirective.Tokens);
|
||||
Assert.Equal("\"hello\"", stringToken.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_AutomaticallyOverridesImportedSingleLineSinglyOccurringDirective_MainDocument()
|
||||
{
|
||||
// Arrange
|
||||
var directive = DirectiveDescriptor.CreateSingleLineDirective(
|
||||
"custom",
|
||||
builder =>
|
||||
{
|
||||
builder.AddStringToken();
|
||||
builder.Usage = DirectiveUsage.FileScopedSinglyOccurring;
|
||||
});
|
||||
var phase = new DefaultRazorIntermediateNodeLoweringPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
b.AddDirective(directive);
|
||||
});
|
||||
var options = RazorParserOptions.Create(builder => builder.Directives.Add(directive));
|
||||
var importSource = TestRazorSourceDocument.Create("@custom \"hello\"", filePath: "import.cshtml");
|
||||
var codeDocument = TestRazorCodeDocument.Create("@custom \"world\"");
|
||||
codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source, options));
|
||||
codeDocument.SetImportSyntaxTrees(new[] { RazorSyntaxTree.Parse(importSource, options) });
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
var customDirectives = documentNode.FindDirectiveReferences(directive);
|
||||
var customDirective = (DirectiveIntermediateNode)Assert.Single(customDirectives).Node;
|
||||
var stringToken = Assert.Single(customDirective.Tokens);
|
||||
Assert.Equal("\"world\"", stringToken.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_AutomaticallyOverridesImportedSingleLineSinglyOccurringDirective_MultipleImports()
|
||||
{
|
||||
// Arrange
|
||||
var directive = DirectiveDescriptor.CreateSingleLineDirective(
|
||||
"custom",
|
||||
builder =>
|
||||
{
|
||||
builder.AddStringToken();
|
||||
builder.Usage = DirectiveUsage.FileScopedSinglyOccurring;
|
||||
});
|
||||
var phase = new DefaultRazorIntermediateNodeLoweringPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
b.AddDirective(directive);
|
||||
});
|
||||
var options = RazorParserOptions.Create(builder => builder.Directives.Add(directive));
|
||||
var importSource1 = TestRazorSourceDocument.Create("@custom \"hello\"", filePath: "import1.cshtml");
|
||||
var importSource2 = TestRazorSourceDocument.Create("@custom \"world\"", filePath: "import2.cshtml");
|
||||
var codeDocument = TestRazorCodeDocument.Create("<p>NonDirective</p>");
|
||||
codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source, options));
|
||||
codeDocument.SetImportSyntaxTrees(new[] { RazorSyntaxTree.Parse(importSource1, options), RazorSyntaxTree.Parse(importSource2, options) });
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
var customDirectives = documentNode.FindDirectiveReferences(directive);
|
||||
var customDirective = (DirectiveIntermediateNode)Assert.Single(customDirectives).Node;
|
||||
var stringToken = Assert.Single(customDirective.Tokens);
|
||||
Assert.Equal("\"world\"", stringToken.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_DoesNotImportNonFileScopedSinglyOccurringDirectives_Block()
|
||||
{
|
||||
// Arrange
|
||||
var codeBlockDirective = DirectiveDescriptor.CreateCodeBlockDirective("code", b => b.AddStringToken());
|
||||
var razorBlockDirective = DirectiveDescriptor.CreateRazorBlockDirective("razor", b => b.AddStringToken());
|
||||
var phase = new DefaultRazorIntermediateNodeLoweringPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
b.AddDirective(codeBlockDirective);
|
||||
b.AddDirective(razorBlockDirective);
|
||||
});
|
||||
var options = RazorParserOptions.Create(builder =>
|
||||
{
|
||||
builder.Directives.Add(codeBlockDirective);
|
||||
builder.Directives.Add(razorBlockDirective);
|
||||
});
|
||||
var importSource = TestRazorSourceDocument.Create(
|
||||
@"@code ""code block"" { }
|
||||
@razor ""razor block"" { }",
|
||||
filePath: "testImports.cshtml");
|
||||
var codeDocument = TestRazorCodeDocument.Create("<p>NonDirective</p>");
|
||||
codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source, options));
|
||||
codeDocument.SetImportSyntaxTrees(new[] { RazorSyntaxTree.Parse(importSource, options) });
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
var directives = documentNode.Children.OfType<DirectiveIntermediateNode>();
|
||||
Assert.Empty(directives);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ErrorsForCodeBlockFileScopedSinglyOccurringDirectives()
|
||||
{
|
||||
// Arrange
|
||||
var directive = DirectiveDescriptor.CreateCodeBlockDirective("custom", b => b.Usage = DirectiveUsage.FileScopedSinglyOccurring);
|
||||
var phase = new DefaultRazorIntermediateNodeLoweringPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
b.AddDirective(directive);
|
||||
});
|
||||
var options = RazorParserOptions.Create(builder => builder.Directives.Add(directive));
|
||||
var importSource = TestRazorSourceDocument.Create("@custom { }", filePath: "import.cshtml");
|
||||
var codeDocument = TestRazorCodeDocument.Create("<p>NonDirective</p>");
|
||||
codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source, options));
|
||||
codeDocument.SetImportSyntaxTrees(new[] { RazorSyntaxTree.Parse(importSource, options) });
|
||||
var expectedDiagnostic = RazorDiagnosticFactory.CreateDirective_BlockDirectiveCannotBeImported("custom");
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
var directives = documentNode.Children.OfType<DirectiveIntermediateNode>();
|
||||
Assert.Empty(directives);
|
||||
var diagnostic = Assert.Single(documentNode.GetAllDiagnostics());
|
||||
Assert.Equal(expectedDiagnostic, diagnostic);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ErrorsForRazorBlockFileScopedSinglyOccurringDirectives()
|
||||
{
|
||||
// Arrange
|
||||
var directive = DirectiveDescriptor.CreateRazorBlockDirective("custom", b => b.Usage = DirectiveUsage.FileScopedSinglyOccurring);
|
||||
var phase = new DefaultRazorIntermediateNodeLoweringPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
b.AddDirective(directive);
|
||||
});
|
||||
var options = RazorParserOptions.Create(builder => builder.Directives.Add(directive));
|
||||
var importSource = TestRazorSourceDocument.Create("@custom { }", filePath: "import.cshtml");
|
||||
var codeDocument = TestRazorCodeDocument.Create("<p>NonDirective</p>");
|
||||
codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source, options));
|
||||
codeDocument.SetImportSyntaxTrees(new[] { RazorSyntaxTree.Parse(importSource, options) });
|
||||
var expectedDiagnostic = RazorDiagnosticFactory.CreateDirective_BlockDirectiveCannotBeImported("custom");
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
var directives = documentNode.Children.OfType<DirectiveIntermediateNode>();
|
||||
Assert.Empty(directives);
|
||||
var diagnostic = Assert.Single(documentNode.GetAllDiagnostics());
|
||||
Assert.Equal(expectedDiagnostic, diagnostic);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ThrowsForMissingDependency_SyntaxTree()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorIntermediateNodeLoweringPhase();
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
});
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
() => phase.Execute(codeDocument),
|
||||
$"The '{nameof(DefaultRazorIntermediateNodeLoweringPhase)}' phase requires a '{nameof(RazorSyntaxTree)}' " +
|
||||
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_CollatesSyntaxDiagnosticsFromSourceDocument()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorIntermediateNodeLoweringPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
});
|
||||
var codeDocument = TestRazorCodeDocument.Create("<p class=@(");
|
||||
codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source));
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
var diagnostic = Assert.Single(documentNode.Diagnostics);
|
||||
Assert.Equal(@"The explicit expression block is missing a closing "")"" character. Make sure you have a matching "")"" character for all the ""("" characters within this block, and that none of the "")"" characters are being interpreted as markup.",
|
||||
diagnostic.GetMessage());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_CollatesSyntaxDiagnosticsFromImportDocuments()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorIntermediateNodeLoweringPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
b.Features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
});
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(codeDocument.Source));
|
||||
codeDocument.SetImportSyntaxTrees(new[]
|
||||
{
|
||||
RazorSyntaxTree.Parse(TestRazorSourceDocument.Create("@ ")),
|
||||
RazorSyntaxTree.Parse(TestRazorSourceDocument.Create("<p @(")),
|
||||
});
|
||||
var options = RazorCodeGenerationOptions.CreateDefault();
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
Assert.Collection(documentNode.Diagnostics,
|
||||
diagnostic =>
|
||||
{
|
||||
Assert.Equal(@"A space or line break was encountered after the ""@"" character. Only valid identifiers, keywords, comments, ""("" and ""{"" are valid at the start of a code block and they must occur immediately following ""@"" with no space in between.",
|
||||
diagnostic.GetMessage());
|
||||
},
|
||||
diagnostic =>
|
||||
{
|
||||
Assert.Equal(@"The explicit expression block is missing a closing "")"" character. Make sure you have a matching "")"" character for all the ""("" characters within this block, and that none of the "")"" characters are being interpreted as markup.",
|
||||
diagnostic.GetMessage());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorOptimizationPhaseTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnInitialized_OrdersPassesInAscendingOrder()
|
||||
{
|
||||
// Arrange & Act
|
||||
var phase = new DefaultRazorOptimizationPhase();
|
||||
|
||||
var first = Mock.Of<IRazorOptimizationPass>(p => p.Order == 15);
|
||||
var second = Mock.Of<IRazorOptimizationPass>(p => p.Order == 17);
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
|
||||
b.Features.Add(second);
|
||||
b.Features.Add(first);
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
phase.Passes,
|
||||
p => Assert.Same(first, p),
|
||||
p => Assert.Same(second, p));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ThrowsForMissingDependency()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorOptimizationPhase();
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
() => phase.Execute(codeDocument),
|
||||
$"The '{nameof(DefaultRazorOptimizationPhase)}' phase requires a '{nameof(DocumentIntermediateNode)}' " +
|
||||
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ExecutesPhasesInOrder()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// We're going to set up mocks to simulate a sequence of passes. We don't care about
|
||||
// what's in the nodes, we're just going to look at the identity via strict mocks.
|
||||
var originalNode = new DocumentIntermediateNode();
|
||||
var firstPassNode = new DocumentIntermediateNode();
|
||||
var secondPassNode = new DocumentIntermediateNode();
|
||||
codeDocument.SetDocumentIntermediateNode(originalNode);
|
||||
|
||||
var firstPass = new Mock<IRazorOptimizationPass>(MockBehavior.Strict);
|
||||
firstPass.SetupGet(m => m.Order).Returns(0);
|
||||
firstPass.SetupProperty(m => m.Engine);
|
||||
firstPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||
{
|
||||
originalNode.Children.Add(firstPassNode);
|
||||
});
|
||||
|
||||
var secondPass = new Mock<IRazorOptimizationPass>(MockBehavior.Strict);
|
||||
secondPass.SetupGet(m => m.Order).Returns(1);
|
||||
secondPass.SetupProperty(m => m.Engine);
|
||||
secondPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||
{
|
||||
// Works only when the first pass has run before this.
|
||||
originalNode.Children[0].Children.Add(secondPassNode);
|
||||
});
|
||||
|
||||
var phase = new DefaultRazorOptimizationPhase();
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
|
||||
b.Features.Add(firstPass.Object);
|
||||
b.Features.Add(secondPass.Object);
|
||||
});
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Same(secondPassNode, codeDocument.GetDocumentIntermediateNode().Children[0].Children[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorParsingPhaseTest
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_AddsSyntaxTree()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorParsingPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty(builder =>
|
||||
{
|
||||
builder.Phases.Add(phase);
|
||||
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorLanguageVersion.Latest, fileKind: null));
|
||||
});
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(codeDocument.GetSyntaxTree());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_UsesConfigureParserFeatures()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorParsingPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty((builder) =>
|
||||
{
|
||||
builder.Phases.Add(phase);
|
||||
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorLanguageVersion.Latest, fileKind: null));
|
||||
builder.Features.Add(new MyParserOptionsFeature());
|
||||
});
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
var directive = Assert.Single(syntaxTree.Options.Directives);
|
||||
Assert.Equal("test", directive.Directive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ParsesImports()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorParsingPhase();
|
||||
var engine = RazorProjectEngine.CreateEmpty((builder) =>
|
||||
{
|
||||
builder.Phases.Add(phase);
|
||||
builder.Features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorLanguageVersion.Latest, fileKind: null));
|
||||
builder.Features.Add(new MyParserOptionsFeature());
|
||||
});
|
||||
|
||||
var imports = new[]
|
||||
{
|
||||
TestRazorSourceDocument.Create(),
|
||||
TestRazorSourceDocument.Create(),
|
||||
};
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.Create(TestRazorSourceDocument.Create(), imports);
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
codeDocument.GetImportSyntaxTrees(),
|
||||
t => { Assert.Same(t.Source, imports[0]); Assert.Equal("test", Assert.Single(t.Options.Directives).Directive); },
|
||||
t => { Assert.Same(t.Source, imports[1]); Assert.Equal("test", Assert.Single(t.Options.Directives).Directive); });
|
||||
}
|
||||
|
||||
private class MyParserOptionsFeature : RazorEngineFeatureBase, IConfigureRazorParserOptionsFeature
|
||||
{
|
||||
public int Order { get; }
|
||||
|
||||
public void Configure(RazorParserOptionsBuilder options)
|
||||
{
|
||||
options.Directives.Add(DirectiveDescriptor.CreateDirective("test", DirectiveKind.SingleLine));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// 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.Linq;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorProjectEngineBuilderTest
|
||||
{
|
||||
[Fact]
|
||||
public void Build_AddsFeaturesToRazorEngine()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorProjectEngineBuilder(RazorConfiguration.Default, Mock.Of<RazorProjectFileSystem>());
|
||||
builder.Features.Add(Mock.Of<IRazorEngineFeature>());
|
||||
builder.Features.Add(Mock.Of<IRazorEngineFeature>());
|
||||
builder.Features.Add(Mock.Of<IRazorProjectEngineFeature>());
|
||||
|
||||
var features = builder.Features.ToArray();
|
||||
|
||||
// Act
|
||||
var projectEngine = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.Collection(projectEngine.Engine.Features,
|
||||
feature => Assert.Same(features[0], feature),
|
||||
feature => Assert.Same(features[1], feature));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_AddsPhasesToRazorEngine()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorProjectEngineBuilder(RazorConfiguration.Default, Mock.Of<RazorProjectFileSystem>());
|
||||
builder.Phases.Add(Mock.Of<IRazorEnginePhase>());
|
||||
builder.Phases.Add(Mock.Of<IRazorEnginePhase>());
|
||||
|
||||
var phases = builder.Phases.ToArray();
|
||||
|
||||
// Act
|
||||
var projectEngine = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.Collection(projectEngine.Engine.Phases,
|
||||
phase => Assert.Same(phases[0], phase),
|
||||
phase => Assert.Same(phases[1], phase));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_CreatesProjectEngineWithFileSystem()
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = Mock.Of<RazorProjectFileSystem>();
|
||||
var builder = new DefaultRazorProjectEngineBuilder(RazorConfiguration.Default, fileSystem);
|
||||
|
||||
// Act
|
||||
var projectEngine = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.Same(fileSystem, projectEngine.FileSystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
// 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 Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorProjectEngineIntegrationTest
|
||||
{
|
||||
[Fact]
|
||||
public void Process_SetsOptions_Runtime()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
var parserOptions = codeDocument.GetParserOptions();
|
||||
Assert.False(parserOptions.DesignTime);
|
||||
|
||||
var codeGenerationOptions = codeDocument.GetCodeGenerationOptions();
|
||||
Assert.False(codeGenerationOptions.DesignTime);
|
||||
Assert.False(codeGenerationOptions.SuppressChecksum);
|
||||
Assert.False(codeGenerationOptions.SuppressMetadataAttributes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessDesignTime_SetsOptions_DesignTime()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(projectItem);
|
||||
|
||||
// Assert
|
||||
var parserOptions = codeDocument.GetParserOptions();
|
||||
Assert.True(parserOptions.DesignTime);
|
||||
|
||||
var codeGenerationOptions = codeDocument.GetCodeGenerationOptions();
|
||||
Assert.True(codeGenerationOptions.DesignTime);
|
||||
Assert.True(codeGenerationOptions.SuppressChecksum);
|
||||
Assert.True(codeGenerationOptions.SuppressMetadataAttributes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_GetsImportsFromFeature()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var testImport = Mock.Of<RazorProjectItem>(i => i.Read() == new MemoryStream() && i.FilePath == "testvalue" && i.Exists == true);
|
||||
var importFeature = new Mock<IImportProjectFeature>();
|
||||
importFeature
|
||||
.Setup(feature => feature.GetImports(It.IsAny<RazorProjectItem>()))
|
||||
.Returns(new[] { testImport });
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty, builder =>
|
||||
{
|
||||
builder.SetImportFeature(importFeature.Object);
|
||||
});
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
var import = Assert.Single(codeDocument.Imports);
|
||||
Assert.Equal("testvalue", import.FilePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_GetsImportsFromFeature_MultipleFeatures()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var testImport1 = Mock.Of<RazorProjectItem>(i => i.Read() == new MemoryStream() && i.FilePath == "testvalue1" && i.Exists == true);
|
||||
var importFeature1 = new Mock<IImportProjectFeature>();
|
||||
importFeature1
|
||||
.Setup(feature => feature.GetImports(It.IsAny<RazorProjectItem>()))
|
||||
.Returns(new[] { testImport1 });
|
||||
|
||||
var testImport2 = Mock.Of<RazorProjectItem>(i => i.Read() == new MemoryStream() && i.FilePath == "testvalue2" && i.Exists == true);
|
||||
var importFeature2 = new Mock<IImportProjectFeature>();
|
||||
importFeature2
|
||||
.Setup(feature => feature.GetImports(It.IsAny<RazorProjectItem>()))
|
||||
.Returns(new[] { testImport2 });
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty, builder =>
|
||||
{
|
||||
builder.Features.Add(importFeature1.Object);
|
||||
builder.Features.Add(importFeature2.Object);
|
||||
});
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(codeDocument.Imports,
|
||||
i => Assert.Equal("testvalue1", i.FilePath),
|
||||
i => Assert.Equal("testvalue2", i.FilePath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_GeneratesCodeDocumentWithValidCSharpDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
var csharpDocument = codeDocument.GetCSharpDocument();
|
||||
Assert.NotNull(csharpDocument);
|
||||
Assert.Empty(csharpDocument.Diagnostics);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_WithImportsAndTagHelpers_SetsOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
var importItem = new TestRazorProjectItem("_import.cshtml");
|
||||
var expectedImports = new[] { RazorSourceDocument.ReadFrom(importItem) };
|
||||
var expectedTagHelpers = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly").Build(),
|
||||
TagHelperDescriptorBuilder.Create("Test2TagHelper", "TestAssembly").Build(),
|
||||
};
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(RazorSourceDocument.ReadFrom(projectItem), "test", expectedImports, expectedTagHelpers);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Same(expectedTagHelpers, tagHelpers);
|
||||
Assert.Equal(expectedImports, codeDocument.Imports);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_WithFileKind_SetsOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(RazorSourceDocument.ReadFrom(projectItem), "test", Array.Empty<RazorSourceDocument>(), tagHelpers: null);
|
||||
|
||||
// Assert
|
||||
var actual = codeDocument.GetFileKind();
|
||||
Assert.Equal("test", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_WithNullTagHelpers_SetsOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(RazorSourceDocument.ReadFrom(projectItem), "test", Array.Empty<RazorSourceDocument>(), tagHelpers: null);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Null(tagHelpers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_SetsNullTagHelpersOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Null(tagHelpers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_SetsInferredFileKindOnCodeDocument_MvcFile()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
var actual = codeDocument.GetFileKind();
|
||||
Assert.Same(FileKinds.Legacy, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_SetsInferredFileKindOnCodeDocument_Component()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.razor");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
var actual = codeDocument.GetFileKind();
|
||||
Assert.Same(FileKinds.Component, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_WithNullImports_SetsEmptyListOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(RazorSourceDocument.ReadFrom(projectItem), "test", importSources: null, tagHelpers: null);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(codeDocument.Imports);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessDesignTime_WithImportsAndTagHelpers_SetsOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
var importItem = new TestRazorProjectItem("_import.cshtml");
|
||||
var expectedImports = new[] { RazorSourceDocument.ReadFrom(importItem) };
|
||||
var expectedTagHelpers = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly").Build(),
|
||||
TagHelperDescriptorBuilder.Create("Test2TagHelper", "TestAssembly").Build(),
|
||||
};
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(RazorSourceDocument.ReadFrom(projectItem), "test", expectedImports, expectedTagHelpers);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Same(expectedTagHelpers, tagHelpers);
|
||||
Assert.Equal(expectedImports, codeDocument.Imports);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessDesignTime_WithNullTagHelpers_SetsOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(RazorSourceDocument.ReadFrom(projectItem), "test", Array.Empty<RazorSourceDocument>(), tagHelpers: null);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Null(tagHelpers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessDesignTime_SetsInferredFileKindOnCodeDocument_MvcFile()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(projectItem);
|
||||
|
||||
// Assert
|
||||
var actual = codeDocument.GetFileKind();
|
||||
Assert.Same(FileKinds.Legacy, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessDesignTime_SetsInferredFileKindOnCodeDocument_Component()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.razor");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(projectItem);
|
||||
|
||||
// Assert
|
||||
var actual = codeDocument.GetFileKind();
|
||||
Assert.Same(FileKinds.Component, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessDesignTime_SetsNullTagHelpersOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(projectItem);
|
||||
|
||||
// Assert
|
||||
var tagHelpers = codeDocument.GetTagHelpers();
|
||||
Assert.Null(tagHelpers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessDesignTime_WithNullImports_SetsEmptyListOnCodeDocument()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new TestRazorProjectItem("Index.cshtml");
|
||||
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, TestRazorProjectFileSystem.Empty);
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.ProcessDesignTime(RazorSourceDocument.ReadFrom(projectItem), "test", importSources: null, tagHelpers: null);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(codeDocument.Imports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// 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 Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorProjectEngineTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetImportSourceDocuments_DoesNotIncludeNonExistentItems()
|
||||
{
|
||||
// Arrange
|
||||
var existingItem = new TestRazorProjectItem("Index.cshtml");
|
||||
var nonExistentItem = Mock.Of<RazorProjectItem>(item => item.Exists == false);
|
||||
var items = new[] { existingItem, nonExistentItem };
|
||||
|
||||
// Act
|
||||
var sourceDocuments = DefaultRazorProjectEngine.GetImportSourceDocuments(items);
|
||||
|
||||
// Assert
|
||||
var sourceDocument = Assert.Single(sourceDocuments);
|
||||
Assert.Equal(existingItem.FilePath, sourceDocument.FilePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetImportSourceDocuments_UnreadableItem_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new Mock<RazorProjectItem>(MockBehavior.Strict);
|
||||
projectItem.SetupGet(p => p.Exists).Returns(true);
|
||||
projectItem.SetupGet(p => p.PhysicalPath).Returns("path/to/file.cshtml");
|
||||
projectItem.Setup(p => p.Read()).Throws(new IOException("Couldn't read file."));
|
||||
var items = new[] { projectItem.Object };
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<IOException>(() => DefaultRazorProjectEngine.GetImportSourceDocuments(items));
|
||||
Assert.Equal("Couldn't read file.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetImportSourceDocuments_WithSuppressExceptions_UnreadableItem_DoesNotThrow()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = new Mock<RazorProjectItem>(MockBehavior.Strict);
|
||||
projectItem.SetupGet(p => p.Exists).Returns(true);
|
||||
projectItem.SetupGet(p => p.PhysicalPath).Returns("path/to/file.cshtml");
|
||||
projectItem.SetupGet(p => p.FilePath).Returns("path/to/file.cshtml");
|
||||
projectItem.SetupGet(p => p.RelativePhysicalPath).Returns("path/to/file.cshtml");
|
||||
projectItem.Setup(p => p.Read()).Throws(new IOException("Couldn't read file."));
|
||||
var items = new[] { projectItem.Object };
|
||||
|
||||
// Act
|
||||
var sourceDocuments = DefaultRazorProjectEngine.GetImportSourceDocuments(items, suppressExceptions: true);
|
||||
|
||||
// Assert - Does not throw
|
||||
Assert.Empty(sourceDocuments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,298 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorProjectFileSystemTest
|
||||
{
|
||||
private static string TestFolder { get; } = Path.Combine(
|
||||
TestProject.GetProjectDirectory(typeof(DefaultRazorProjectFileSystemTest)),
|
||||
"TestFiles",
|
||||
"DefaultRazorProjectFileSystem");
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
public void NormalizeAndEnsureValidPath_ThrowsIfPathIsNullOrEmpty(string path)
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = new TestRazorProjectFileSystem("C:/some/test/path/root");
|
||||
|
||||
// Act and Assert
|
||||
ExceptionAssert.ThrowsArgumentNullOrEmptyString(() => fileSystem.NormalizeAndEnsureValidPath(path), "path");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NormalizeAndEnsureValidPath_NormalizesToAbsolutePath()
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = new TestRazorProjectFileSystem("C:/some/test/path/root");
|
||||
|
||||
// Act
|
||||
var absolutePath = fileSystem.NormalizeAndEnsureValidPath("file.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("C:/some/test/path/root/file.cshtml", absolutePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NormalizeAndEnsureValidPath_FileFromNetworkShare__WindowsStyle_NormalizesToAbsolutePath()
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = new TestRazorProjectFileSystem("//some/network/share/root");
|
||||
|
||||
// Act
|
||||
var absolutePath = fileSystem.NormalizeAndEnsureValidPath("\\\\some\\network\\share\\root\\file.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("//some/network/share/root/file.cshtml", absolutePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NormalizeAndEnsureValidPath_FileFromNetworkShare_UnixStyle_NormalizesToAbsolutePath()
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = new TestRazorProjectFileSystem("//some/network/share/root");
|
||||
|
||||
// Act
|
||||
var absolutePath = fileSystem.NormalizeAndEnsureValidPath("//some/network/share/root/file.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("//some/network/share/root/file.cshtml", absolutePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NormalizeAndEnsureValidPath_NormalizesToAbsolutePathWithoutForwardSlash()
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = new TestRazorProjectFileSystem("C:/some/test/path/root");
|
||||
|
||||
// Act
|
||||
var absolutePath = fileSystem.NormalizeAndEnsureValidPath("/file.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("C:/some/test/path/root/file.cshtml", absolutePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NormalizeAndEnsureValidPath_NormalizesToForwardSlashes()
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = new TestRazorProjectFileSystem(@"C:\some\test\path\root");
|
||||
|
||||
// Act
|
||||
var absolutePath = fileSystem.NormalizeAndEnsureValidPath(@"something\file.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("C:/some/test/path/root/something/file.cshtml", absolutePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnumerateItems_DiscoversAllCshtmlFiles()
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = new DefaultRazorProjectFileSystem(TestFolder);
|
||||
|
||||
// Act
|
||||
var items = fileSystem.EnumerateItems("/");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
items.OrderBy(f => f.FilePath, StringComparer.Ordinal),
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/Home.cshtml", item.FilePath);
|
||||
Assert.Equal("/", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Home.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal("Home.cshtml", item.RelativePhysicalPath);
|
||||
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/Views/About/About.cshtml", item.FilePath);
|
||||
Assert.Equal("/", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Views", "About", "About.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal(Path.Combine("Views", "About", "About.cshtml"), item.RelativePhysicalPath);
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/Views/Home/Index.cshtml", item.FilePath);
|
||||
Assert.Equal("/", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Views", "Home", "Index.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal(Path.Combine("Views", "Home", "Index.cshtml"), item.RelativePhysicalPath);
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/Views/Home/_ViewImports.cshtml", item.FilePath);
|
||||
Assert.Equal("/", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Views", "Home", "_ViewImports.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal(Path.Combine("Views", "Home", "_ViewImports.cshtml"), item.RelativePhysicalPath);
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/Views/_ViewImports.cshtml", item.FilePath);
|
||||
Assert.Equal("/", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Views", "_ViewImports.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal(Path.Combine("Views", "_ViewImports.cshtml"), item.RelativePhysicalPath);
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/_ViewImports.cshtml", item.FilePath);
|
||||
Assert.Equal("/", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "_ViewImports.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal("_ViewImports.cshtml", item.RelativePhysicalPath);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnumerateItems_DiscoversAllCshtmlFiles_UnderSpecifiedBasePath()
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = new DefaultRazorProjectFileSystem(TestFolder);
|
||||
|
||||
// Act
|
||||
var items = fileSystem.EnumerateItems("/Views");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
items.OrderBy(f => f.FilePath, StringComparer.Ordinal),
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/About/About.cshtml", item.FilePath);
|
||||
Assert.Equal("/Views", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Views", "About", "About.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal(Path.Combine("About", "About.cshtml"), item.RelativePhysicalPath);
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/Home/Index.cshtml", item.FilePath);
|
||||
Assert.Equal("/Views", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Views", "Home", "Index.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal(Path.Combine("Home", "Index.cshtml"), item.RelativePhysicalPath);
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/Home/_ViewImports.cshtml", item.FilePath);
|
||||
Assert.Equal("/Views", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Views", "Home", "_ViewImports.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal(Path.Combine("Home", "_ViewImports.cshtml"), item.RelativePhysicalPath);
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/_ViewImports.cshtml", item.FilePath);
|
||||
Assert.Equal("/Views", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Views", "_ViewImports.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal(Path.Combine("_ViewImports.cshtml"), item.RelativePhysicalPath);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnumerateItems_ReturnsEmptySequence_WhenBasePathDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = new DefaultRazorProjectFileSystem(TestFolder);
|
||||
|
||||
// Act
|
||||
var items = fileSystem.EnumerateItems("/Does-Not-Exist");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(items);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindHierarchicalItems_FindsItemsWithMatchingNames()
|
||||
{
|
||||
// Arrange
|
||||
var fileSystem = new DefaultRazorProjectFileSystem(TestFolder);
|
||||
|
||||
// Act
|
||||
var items = fileSystem.FindHierarchicalItems("/Views/Home/Index.cshtml", "_ViewImports.cshtml");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
items,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/Views/Home/_ViewImports.cshtml", item.FilePath);
|
||||
Assert.Equal("/", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Views", "Home", "_ViewImports.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal(Path.Combine("Views", "Home", "_ViewImports.cshtml"), item.RelativePhysicalPath);
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/Views/_ViewImports.cshtml", item.FilePath);
|
||||
Assert.Equal("/", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Views", "_ViewImports.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal(Path.Combine("Views", "_ViewImports.cshtml"), item.RelativePhysicalPath);
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/_ViewImports.cshtml", item.FilePath);
|
||||
Assert.Equal("/", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "_ViewImports.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal("_ViewImports.cshtml", item.RelativePhysicalPath);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItem_ReturnsFileFromDisk()
|
||||
{
|
||||
// Arrange
|
||||
var filePath = "/Views/About/About.cshtml";
|
||||
var fileSystem = new DefaultRazorProjectFileSystem(TestFolder);
|
||||
|
||||
// Act
|
||||
var item = fileSystem.GetItem(filePath, fileKind: null);
|
||||
|
||||
// Assert
|
||||
Assert.True(item.Exists);
|
||||
Assert.Equal(filePath, item.FilePath);
|
||||
Assert.Equal("/", item.BasePath);
|
||||
Assert.Equal(Path.Combine(TestFolder, "Views", "About", "About.cshtml"), item.PhysicalPath);
|
||||
Assert.Equal(Path.Combine("Views", "About", "About.cshtml"), item.RelativePhysicalPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItem_ReturnsNotFoundResult()
|
||||
{
|
||||
// Arrange
|
||||
var path = "/NotFound.cshtml";
|
||||
var fileSystem = new DefaultRazorProjectFileSystem(TestFolder);
|
||||
|
||||
// Act
|
||||
var item = fileSystem.GetItem(path, fileKind: null);
|
||||
|
||||
// Assert
|
||||
Assert.False(item.Exists);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetItem_MismatchedRootPath_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var rootPath = "//some/network/share/root";
|
||||
var fileSystem = new TestRazorProjectFileSystem(rootPath);
|
||||
var path = "\\\\some\\other\\network\\share\\root\\file.cshtml";
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
() => fileSystem.GetItem(path, fileKind: null),
|
||||
$"The file '{path.Replace('\\', '/')}' is not a descendent of the base path '{rootPath}'.");
|
||||
}
|
||||
|
||||
private class TestRazorProjectFileSystem : DefaultRazorProjectFileSystem
|
||||
{
|
||||
public TestRazorProjectFileSystem(string root) : base(root)
|
||||
{
|
||||
}
|
||||
|
||||
public new string NormalizeAndEnsureValidPath(string path) => base.NormalizeAndEnsureValidPath(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
// 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.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorProjectItemTest
|
||||
{
|
||||
private static string TestFolder { get; } = Path.Combine(
|
||||
TestProject.GetProjectDirectory(typeof(DefaultRazorProjectItemTest)),
|
||||
"TestFiles",
|
||||
"DefaultRazorProjectFileSystem");
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorProjectItem_SetsProperties()
|
||||
{
|
||||
// Arrange
|
||||
var fileInfo = new FileInfo(Path.Combine(TestFolder, "Home.cshtml"));
|
||||
|
||||
// Act
|
||||
var projectItem = new DefaultRazorProjectItem("/", "/Home.cshtml", "Home.cshtml", "test", fileInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home.cshtml", projectItem.FilePath);
|
||||
Assert.Equal("/", projectItem.BasePath);
|
||||
Assert.True(projectItem.Exists);
|
||||
Assert.Equal("Home.cshtml", projectItem.FileName);
|
||||
Assert.Equal("test", projectItem.FileKind);
|
||||
Assert.Equal(fileInfo.FullName, projectItem.PhysicalPath);
|
||||
Assert.Equal("Home.cshtml", projectItem.RelativePhysicalPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorProjectItem_InfersFileKind_Component()
|
||||
{
|
||||
// Arrange
|
||||
var fileInfo = new FileInfo(Path.Combine(TestFolder, "Home.cshtml"));
|
||||
|
||||
// Act
|
||||
var projectItem = new DefaultRazorProjectItem("/", "/Home.razor", "Home.cshtml", fileKind: null, fileInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(FileKinds.Component, projectItem.FileKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorProjectItem_InfersFileKind_Legacy()
|
||||
{
|
||||
// Arrange
|
||||
var fileInfo = new FileInfo(Path.Combine(TestFolder, "Home.cshtml"));
|
||||
|
||||
// Act
|
||||
var projectItem = new DefaultRazorProjectItem("/", "/Home.cshtml", "Home.cshtml", fileKind: null, fileInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(FileKinds.Legacy, projectItem.FileKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultRazorProjectItem_InfersFileKind_Null()
|
||||
{
|
||||
// Arrange
|
||||
var fileInfo = new FileInfo(Path.Combine(TestFolder, "Home.cshtml"));
|
||||
|
||||
// Act
|
||||
var projectItem = new DefaultRazorProjectItem("/", filePath: null, "Home.cshtml", fileKind: null, fileInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Null(projectItem.FileKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Exists_ReturnsFalseWhenFileDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var fileInfo = new FileInfo(Path.Combine(TestFolder, "Views", "FileDoesNotExist.cshtml"));
|
||||
|
||||
// Act
|
||||
var projectItem = new DefaultRazorProjectItem("/Views", "/FileDoesNotExist.cshtml", Path.Combine("Views", "FileDoesNotExist.cshtml"), "test", fileInfo);
|
||||
|
||||
// Assert
|
||||
Assert.False(projectItem.Exists);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Read_ReturnsReadStream()
|
||||
{
|
||||
// Arrange
|
||||
var fileInfo = new FileInfo(Path.Combine(TestFolder, "Home.cshtml"));
|
||||
var projectItem = new DefaultRazorProjectItem("/", "/Home.cshtml", "Home.cshtml", "test", fileInfo);
|
||||
|
||||
// Act
|
||||
var stream = projectItem.Read();
|
||||
|
||||
// Assert
|
||||
Assert.Equal("home-content", new StreamReader(stream).ReadToEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
// 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 Microsoft.AspNetCore.Testing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRazorSyntaxTreePhaseTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnInitialized_OrdersPassesInAscendingOrder()
|
||||
{
|
||||
// Arrange & Act
|
||||
var phase = new DefaultRazorSyntaxTreePhase();
|
||||
|
||||
var first = Mock.Of<IRazorSyntaxTreePass>(p => p.Order == 15);
|
||||
var second = Mock.Of<IRazorSyntaxTreePass>(p => p.Order == 17);
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
|
||||
b.Features.Add(second);
|
||||
b.Features.Add(first);
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
phase.Passes,
|
||||
p => Assert.Same(first, p),
|
||||
p => Assert.Same(second, p));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ThrowsForMissingDependency()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorSyntaxTreePhase();
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
() => phase.Execute(codeDocument),
|
||||
$"The '{nameof(DefaultRazorSyntaxTreePhase)}' phase requires a '{nameof(RazorSyntaxTree)}' " +
|
||||
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ExecutesPhasesInOrder()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// We're going to set up mocks to simulate a sequence of passes. We don't care about
|
||||
// what's in the trees, we're just going to look at the identity via strict mocks.
|
||||
var originalSyntaxTree = RazorSyntaxTree.Parse(codeDocument.Source);
|
||||
var firstPassSyntaxTree = RazorSyntaxTree.Parse(codeDocument.Source);
|
||||
var secondPassSyntaxTree = RazorSyntaxTree.Parse(codeDocument.Source);
|
||||
codeDocument.SetSyntaxTree(originalSyntaxTree);
|
||||
|
||||
var firstPass = new Mock<IRazorSyntaxTreePass>(MockBehavior.Strict);
|
||||
firstPass.SetupGet(m => m.Order).Returns(0);
|
||||
firstPass.SetupProperty(m => m.Engine);
|
||||
firstPass.Setup(m => m.Execute(codeDocument, originalSyntaxTree)).Returns(firstPassSyntaxTree);
|
||||
|
||||
var secondPass = new Mock<IRazorSyntaxTreePass>(MockBehavior.Strict);
|
||||
secondPass.SetupGet(m => m.Order).Returns(1);
|
||||
secondPass.SetupProperty(m => m.Engine);
|
||||
secondPass.Setup(m => m.Execute(codeDocument, firstPassSyntaxTree)).Returns(secondPassSyntaxTree);
|
||||
|
||||
var phase = new DefaultRazorSyntaxTreePhase();
|
||||
|
||||
var engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
|
||||
b.Features.Add(firstPass.Object);
|
||||
b.Features.Add(secondPass.Object);
|
||||
});
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Same(secondPassSyntaxTree, codeDocument.GetSyntaxTree());
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,44 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DefaultRequiredAttributeDescriptorBuilderTest
|
||||
{
|
||||
[Fact]
|
||||
public void Build_DisplayNameIsName_NameComparisonFullMatch()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRequiredAttributeDescriptorBuilder();
|
||||
|
||||
builder
|
||||
.Name("asp-action")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch);
|
||||
|
||||
// Act
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.Equal("asp-action", descriptor.DisplayName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_DisplayNameIsNameWithDots_NameComparisonPrefixMatch()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRequiredAttributeDescriptorBuilder();
|
||||
|
||||
builder
|
||||
.Name("asp-route-")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch);
|
||||
|
||||
// Act
|
||||
var descriptor = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.Equal("asp-route-...", descriptor.DisplayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DirectiveDescriptorBuilderExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void AddMemberToken_AddsToken()
|
||||
{
|
||||
// Arrange & Act
|
||||
var descriptor = DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddMemberToken());
|
||||
|
||||
// Assert
|
||||
var token = Assert.Single(descriptor.Tokens);
|
||||
Assert.Equal(DirectiveTokenKind.Member, token.Kind);
|
||||
Assert.False(token.Optional);
|
||||
Assert.Null(token.Name);
|
||||
Assert.Null(token.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddNamespaceToken_AddsToken()
|
||||
{
|
||||
// Arrange & Act
|
||||
var descriptor = DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddNamespaceToken("Name", "Description"));
|
||||
|
||||
// Assert
|
||||
var token = Assert.Single(descriptor.Tokens);
|
||||
Assert.Equal(DirectiveTokenKind.Namespace, token.Kind);
|
||||
Assert.False(token.Optional);
|
||||
Assert.Equal("Name", token.Name);
|
||||
Assert.Equal("Description", token.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddStringToken_AddsToken()
|
||||
{
|
||||
// Arrange & Act
|
||||
var descriptor = DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddStringToken());
|
||||
|
||||
// Assert
|
||||
var token = Assert.Single(descriptor.Tokens);
|
||||
Assert.Equal(DirectiveTokenKind.String, token.Kind);
|
||||
Assert.False(token.Optional);
|
||||
Assert.Null(token.Name);
|
||||
Assert.Null(token.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddTypeToken_AddsToken()
|
||||
{
|
||||
// Arrange & Act
|
||||
var descriptor = DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddTypeToken("Name", "Description"));
|
||||
|
||||
// Assert
|
||||
var token = Assert.Single(descriptor.Tokens);
|
||||
Assert.Equal(DirectiveTokenKind.Type, token.Kind);
|
||||
Assert.False(token.Optional);
|
||||
Assert.Equal("Name", token.Name);
|
||||
Assert.Equal("Description", token.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOptionalTypeToken_AddsToken()
|
||||
{
|
||||
// Arrange & Act
|
||||
var descriptor = DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddOptionalTypeToken());
|
||||
|
||||
// Assert
|
||||
var token = Assert.Single(descriptor.Tokens);
|
||||
Assert.Equal(DirectiveTokenKind.Type, token.Kind);
|
||||
Assert.True(token.Optional);
|
||||
Assert.Null(token.Name);
|
||||
Assert.Null(token.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOptionalMemberToken_AddsToken()
|
||||
{
|
||||
// Arrange & Act
|
||||
var descriptor = DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddOptionalMemberToken("Name", "Description"));
|
||||
|
||||
// Assert
|
||||
var token = Assert.Single(descriptor.Tokens);
|
||||
Assert.Equal(DirectiveTokenKind.Member, token.Kind);
|
||||
Assert.True(token.Optional);
|
||||
Assert.Equal("Name", token.Name);
|
||||
Assert.Equal("Description", token.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOptionalNamespaceToken_AddsToken()
|
||||
{
|
||||
// Arrange & Act
|
||||
var descriptor = DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddOptionalNamespaceToken());
|
||||
|
||||
// Assert
|
||||
var token = Assert.Single(descriptor.Tokens);
|
||||
Assert.Equal(DirectiveTokenKind.Namespace, token.Kind);
|
||||
Assert.True(token.Optional);
|
||||
Assert.Null(token.Name);
|
||||
Assert.Null(token.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOptionalStringToken_AddsToken()
|
||||
{
|
||||
// Arrange & Act
|
||||
var descriptor = DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddOptionalStringToken("Name", "Description"));
|
||||
|
||||
// Assert
|
||||
var token = Assert.Single(descriptor.Tokens);
|
||||
Assert.Equal(DirectiveTokenKind.String, token.Kind);
|
||||
Assert.True(token.Optional);
|
||||
Assert.Equal("Name", token.Name);
|
||||
Assert.Equal("Description", token.Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DirectiveDescriptorTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateDirective_CreatesDirective_WithProvidedKind()
|
||||
{
|
||||
// Arrange & Act
|
||||
var directive = DirectiveDescriptor.CreateDirective("test", DirectiveKind.SingleLine);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test", directive.Directive);
|
||||
Assert.Equal(DirectiveKind.SingleLine, directive.Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDirective_WithConfigure_CreatesDirective_WithProvidedKind()
|
||||
{
|
||||
// Arrange
|
||||
var called = false;
|
||||
Action<IDirectiveDescriptorBuilder> configure = b => { called = true; };
|
||||
|
||||
// Act
|
||||
var directive = DirectiveDescriptor.CreateDirective("test", DirectiveKind.SingleLine, configure);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test", directive.Directive);
|
||||
Assert.Equal(DirectiveKind.SingleLine, directive.Kind);
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateSingleLineDirective_CreatesSingleLineDirective()
|
||||
{
|
||||
// Arrange & Act
|
||||
var directive = DirectiveDescriptor.CreateSingleLineDirective("test");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test", directive.Directive);
|
||||
Assert.Equal(DirectiveKind.SingleLine, directive.Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateSingleLineDirective_WithConfigure_CreatesSingleLineDirective()
|
||||
{
|
||||
// Arrange
|
||||
var called = false;
|
||||
Action<IDirectiveDescriptorBuilder> configure = b => { called = true; };
|
||||
|
||||
// Act
|
||||
var directive = DirectiveDescriptor.CreateSingleLineDirective("test", configure);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test", directive.Directive);
|
||||
Assert.Equal(DirectiveKind.SingleLine, directive.Kind);
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateRazorBlockDirective_CreatesRazorBlockDirective()
|
||||
{
|
||||
// Arrange & Act
|
||||
var directive = DirectiveDescriptor.CreateRazorBlockDirective("test");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test", directive.Directive);
|
||||
Assert.Equal(DirectiveKind.RazorBlock, directive.Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateRazorBlockDirective_WithConfigure_CreatesRazorBlockDirective()
|
||||
{
|
||||
// Arrange
|
||||
var called = false;
|
||||
Action<IDirectiveDescriptorBuilder> configure = b => { called = true; };
|
||||
|
||||
// Act
|
||||
var directive = DirectiveDescriptor.CreateRazorBlockDirective("test", configure);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test", directive.Directive);
|
||||
Assert.Equal(DirectiveKind.RazorBlock, directive.Kind);
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateCodeBlockDirective_CreatesCodeBlockDirective()
|
||||
{
|
||||
// Arrange & Act
|
||||
var directive = DirectiveDescriptor.CreateCodeBlockDirective("test");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test", directive.Directive);
|
||||
Assert.Equal(DirectiveKind.CodeBlock, directive.Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateCodeBlockDirective_WithConfigure_CreatesCodeBlockDirective()
|
||||
{
|
||||
// Arrange
|
||||
var called = false;
|
||||
Action<IDirectiveDescriptorBuilder> configure = b => { called = true; };
|
||||
|
||||
// Act
|
||||
var directive = DirectiveDescriptor.CreateCodeBlockDirective("test", configure);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test", directive.Directive);
|
||||
Assert.Equal(DirectiveKind.CodeBlock, directive.Kind);
|
||||
Assert.True(called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_ValidatesDirectiveKeyword_EmptyIsInvalid()
|
||||
{
|
||||
// Arrange & Act
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => DirectiveDescriptor.CreateSingleLineDirective(""));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Invalid directive keyword ''. Directives must have a non-empty keyword that consists only of letters.", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_ValidatesDirectiveKeyword_InvalidCharacter()
|
||||
{
|
||||
// Arrange & Act
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => DirectiveDescriptor.CreateSingleLineDirective("test_directive"));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Invalid directive keyword 'test_directive'. Directives must have a non-empty keyword that consists only of letters.", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_ValidatesDirectiveName_NonOptionalTokenFollowsOptionalToken()
|
||||
{
|
||||
// Arrange & Act
|
||||
var ex = Assert.Throws<InvalidOperationException>(
|
||||
() => DirectiveDescriptor.CreateSingleLineDirective("test", b => { b.AddOptionalMemberToken(); b.AddMemberToken(); }));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("A non-optional directive token cannot follow an optional directive token.", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
using static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeAssert;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DirectiveRemovalOptimizationPassTest
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_Custom_RemovesDirectiveNodeFromDocument()
|
||||
{
|
||||
// Arrange
|
||||
var content = "@custom \"Hello\"";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var defaultEngine = RazorProjectEngine.Create(b =>
|
||||
{
|
||||
b.AddDirective(DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, d => d.AddStringToken()));
|
||||
}).Engine;
|
||||
var documentNode = Lower(codeDocument, defaultEngine);
|
||||
var pass = new DirectiveRemovalOptimizationPass()
|
||||
{
|
||||
Engine = defaultEngine,
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, documentNode);
|
||||
|
||||
// Assert
|
||||
Children(documentNode,
|
||||
node => Assert.IsType<NamespaceDeclarationIntermediateNode>(node));
|
||||
var @namespace = documentNode.Children[0];
|
||||
Children(@namespace,
|
||||
node => Assert.IsType<ClassDeclarationIntermediateNode>(node));
|
||||
var @class = @namespace.Children[0];
|
||||
var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
|
||||
Assert.Empty(method.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_MultipleCustomDirectives_RemovesDirectiveNodesFromDocument()
|
||||
{
|
||||
// Arrange
|
||||
var content = "@custom \"Hello\"" + Environment.NewLine + "@custom \"World\"";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var defaultEngine = RazorProjectEngine.Create(b =>
|
||||
{
|
||||
b.AddDirective(DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, d => d.AddStringToken()));
|
||||
}).Engine;
|
||||
var documentNode = Lower(codeDocument, defaultEngine);
|
||||
var pass = new DirectiveRemovalOptimizationPass()
|
||||
{
|
||||
Engine = defaultEngine,
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, documentNode);
|
||||
|
||||
// Assert
|
||||
Children(documentNode,
|
||||
node => Assert.IsType<NamespaceDeclarationIntermediateNode>(node));
|
||||
var @namespace = documentNode.Children[0];
|
||||
Children(@namespace,
|
||||
node => Assert.IsType<ClassDeclarationIntermediateNode>(node));
|
||||
var @class = @namespace.Children[0];
|
||||
var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
|
||||
Assert.Empty(method.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_DirectiveWithError_PreservesDiagnosticsAndRemovesDirectiveNodeFromDocument()
|
||||
{
|
||||
// Arrange
|
||||
var content = "@custom \"Hello\"";
|
||||
var expectedDiagnostic = RazorDiagnostic.Create(new RazorDiagnosticDescriptor("RZ9999", () => "Some diagnostic message.", RazorDiagnosticSeverity.Error), SourceSpan.Undefined);
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
var defaultEngine = RazorProjectEngine.Create(b =>
|
||||
{
|
||||
b.AddDirective(DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, d => d.AddStringToken()));
|
||||
}).Engine;
|
||||
var documentNode = Lower(codeDocument, defaultEngine);
|
||||
|
||||
// Add the diagnostic to the directive node.
|
||||
var directiveNode = documentNode.FindDescendantNodes<DirectiveIntermediateNode>().Single();
|
||||
directiveNode.Diagnostics.Add(expectedDiagnostic);
|
||||
|
||||
var pass = new DirectiveRemovalOptimizationPass()
|
||||
{
|
||||
Engine = defaultEngine,
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, documentNode);
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(documentNode.Diagnostics);
|
||||
Assert.Equal(expectedDiagnostic, diagnostic);
|
||||
|
||||
Children(documentNode,
|
||||
node => Assert.IsType<NamespaceDeclarationIntermediateNode>(node));
|
||||
var @namespace = documentNode.Children[0];
|
||||
Children(@namespace,
|
||||
node => Assert.IsType<ClassDeclarationIntermediateNode>(node));
|
||||
var @class = @namespace.Children[0];
|
||||
var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
|
||||
Assert.Empty(method.Children);
|
||||
}
|
||||
|
||||
private static DocumentIntermediateNode Lower(RazorCodeDocument codeDocument, RazorEngine engine)
|
||||
{
|
||||
for (var i = 0; i < engine.Phases.Count; i++)
|
||||
{
|
||||
var phase = engine.Phases[i];
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
if (phase is IRazorDirectiveClassifierPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
Assert.NotNull(documentNode);
|
||||
|
||||
return documentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.AspNetCore.Razor.Language.Syntax;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Test
|
||||
{
|
||||
public class DirectiveTokenEditHandlerTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0, 4, "")] // "Namespace"
|
||||
[InlineData(4, 0, "Other")] // "SomeOtherNamespace"
|
||||
[InlineData(0, 4, "Other")] // "OtherNamespace"
|
||||
public void CanAcceptChange_ProvisionallyAcceptsNonWhitespaceChanges(int index, int length, string newText)
|
||||
{
|
||||
// Arrange
|
||||
var directiveTokenHandler = new TestDirectiveTokenEditHandler();
|
||||
directiveTokenHandler.AcceptedCharacters = AcceptedCharactersInternal.NonWhitespace;
|
||||
|
||||
var target = GetSyntaxNode(directiveTokenHandler, "SomeNamespace");
|
||||
|
||||
var sourceChange = new SourceChange(index, length, newText);
|
||||
|
||||
// Act
|
||||
var result = directiveTokenHandler.CanAcceptChange(target, sourceChange);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(PartialParseResultInternal.Accepted | PartialParseResultInternal.Provisional, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(4, 1, "")] // "SomeNamespace"
|
||||
[InlineData(9, 0, " ")] // "Some Name space"
|
||||
[InlineData(9, 5, " Space")] // "Some Name Space"
|
||||
public void CanAcceptChange_RejectsWhitespaceChanges(int index, int length, string newText)
|
||||
{
|
||||
// Arrange
|
||||
var directiveTokenHandler = new TestDirectiveTokenEditHandler();
|
||||
directiveTokenHandler.AcceptedCharacters = AcceptedCharactersInternal.NonWhitespace;
|
||||
|
||||
var target = GetSyntaxNode(directiveTokenHandler, "Some Namespace");
|
||||
|
||||
var sourceChange = new SourceChange(index, length, newText);
|
||||
|
||||
// Act
|
||||
var result = directiveTokenHandler.CanAcceptChange(target, sourceChange);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(PartialParseResultInternal.Rejected, result);
|
||||
}
|
||||
|
||||
private static CSharpStatementLiteralSyntax GetSyntaxNode(DirectiveTokenEditHandler editHandler, string content)
|
||||
{
|
||||
var builder = SyntaxListBuilder<SyntaxToken>.Create();
|
||||
var tokens = CSharpLanguageCharacteristics.Instance.TokenizeString(content).ToArray();
|
||||
foreach (var token in tokens)
|
||||
{
|
||||
builder.Add((SyntaxToken)token.CreateRed());
|
||||
}
|
||||
var node = SyntaxFactory.CSharpStatementLiteral(builder.ToList());
|
||||
|
||||
var context = new SpanContext(SpanChunkGenerator.Null, editHandler);
|
||||
|
||||
return node.WithSpanContext(context);
|
||||
}
|
||||
|
||||
private class TestDirectiveTokenEditHandler : DirectiveTokenEditHandler
|
||||
{
|
||||
public TestDirectiveTokenEditHandler() : base(content => TestTokenizer(content))
|
||||
{
|
||||
}
|
||||
|
||||
public new PartialParseResultInternal CanAcceptChange(SyntaxNode target, SourceChange change)
|
||||
=> base.CanAcceptChange(target, change);
|
||||
|
||||
internal static IEnumerable<Syntax.InternalSyntax.SyntaxToken> TestTokenizer(string str)
|
||||
{
|
||||
yield return Syntax.InternalSyntax.SyntaxFactory.Token(SyntaxKind.Marker, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
using static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeAssert;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class DocumentClassifierPassBaseTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Latest;
|
||||
|
||||
public RazorEngine Engine => CreateProjectEngine().Engine;
|
||||
|
||||
[Fact]
|
||||
public void Execute_HasDocumentKind_IgnoresDocument()
|
||||
{
|
||||
// Arrange
|
||||
var documentNode = new DocumentIntermediateNode()
|
||||
{
|
||||
DocumentKind = "ignore",
|
||||
Options = RazorCodeGenerationOptions.CreateDefault(),
|
||||
};
|
||||
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = Engine;
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), documentNode);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("ignore", documentNode.DocumentKind);
|
||||
NoChildren(documentNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_NoMatch_IgnoresDocument()
|
||||
{
|
||||
// Arrange
|
||||
var documentNode = new DocumentIntermediateNode()
|
||||
{
|
||||
Options = RazorCodeGenerationOptions.CreateDefault(),
|
||||
};
|
||||
|
||||
var pass = new TestDocumentClassifierPass()
|
||||
{
|
||||
Engine = Engine,
|
||||
ShouldMatch = false,
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), documentNode);
|
||||
|
||||
// Assert
|
||||
Assert.Null(documentNode.DocumentKind);
|
||||
NoChildren(documentNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_Match_AddsGlobalTargetExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var documentNode = new DocumentIntermediateNode()
|
||||
{
|
||||
Options = RazorCodeGenerationOptions.CreateDefault(),
|
||||
};
|
||||
|
||||
var expected = new ICodeTargetExtension[]
|
||||
{
|
||||
new MyExtension1(),
|
||||
new MyExtension2(),
|
||||
};
|
||||
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = RazorProjectEngine.CreateEmpty(b =>
|
||||
{
|
||||
for (var i = 0; i < expected.Length; i++)
|
||||
{
|
||||
b.AddTargetExtension(expected[i]);
|
||||
}
|
||||
}).Engine;
|
||||
|
||||
ICodeTargetExtension[] extensions = null;
|
||||
|
||||
pass.CodeTargetCallback = (builder) => extensions = builder.TargetExtensions.ToArray();
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), documentNode);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, extensions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_Match_SetsDocumentType_AndCreatesStructure()
|
||||
{
|
||||
// Arrange
|
||||
var documentNode = new DocumentIntermediateNode()
|
||||
{
|
||||
Options = RazorCodeGenerationOptions.CreateDefault(),
|
||||
};
|
||||
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = Engine;
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), documentNode);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test", documentNode.DocumentKind);
|
||||
Assert.NotNull(documentNode.Target);
|
||||
|
||||
var @namespace = SingleChild<NamespaceDeclarationIntermediateNode>(documentNode);
|
||||
var @class = SingleChild<ClassDeclarationIntermediateNode>(@namespace);
|
||||
var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
|
||||
NoChildren(method);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_AddsUsingsToNamespace()
|
||||
{
|
||||
// Arrange
|
||||
var documentNode = new DocumentIntermediateNode()
|
||||
{
|
||||
Options = RazorCodeGenerationOptions.CreateDefault(),
|
||||
};
|
||||
|
||||
var builder = IntermediateNodeBuilder.Create(documentNode);
|
||||
builder.Add(new UsingDirectiveIntermediateNode());
|
||||
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = Engine;
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), documentNode);
|
||||
|
||||
// Assert
|
||||
var @namespace = SingleChild<NamespaceDeclarationIntermediateNode>(documentNode);
|
||||
Children(
|
||||
@namespace,
|
||||
n => Assert.IsType<UsingDirectiveIntermediateNode>(n),
|
||||
n => Assert.IsType<ClassDeclarationIntermediateNode>(n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_AddsTheRestToMethod()
|
||||
{
|
||||
// Arrange
|
||||
var documentNode = new DocumentIntermediateNode()
|
||||
{
|
||||
Options = RazorCodeGenerationOptions.CreateDefault(),
|
||||
};
|
||||
|
||||
var builder = IntermediateNodeBuilder.Create(documentNode);
|
||||
builder.Add(new HtmlContentIntermediateNode());
|
||||
builder.Add(new CSharpCodeIntermediateNode());
|
||||
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = Engine;
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), documentNode);
|
||||
|
||||
// Assert
|
||||
var @namespace = SingleChild<NamespaceDeclarationIntermediateNode>(documentNode);
|
||||
var @class = SingleChild<ClassDeclarationIntermediateNode>(@namespace);
|
||||
var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
|
||||
Children(
|
||||
method,
|
||||
n => Assert.IsType<HtmlContentIntermediateNode>(n),
|
||||
n => Assert.IsType<CSharpCodeIntermediateNode>(n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_CanInitializeDefaults()
|
||||
{
|
||||
// Arrange
|
||||
var documentNode = new DocumentIntermediateNode()
|
||||
{
|
||||
Options = RazorCodeGenerationOptions.CreateDefault(),
|
||||
};
|
||||
|
||||
var builder = IntermediateNodeBuilder.Create(documentNode);
|
||||
builder.Add(new HtmlContentIntermediateNode());
|
||||
builder.Add(new CSharpCodeIntermediateNode());
|
||||
|
||||
var pass = new TestDocumentClassifierPass()
|
||||
{
|
||||
Engine = Engine,
|
||||
Namespace = "TestNamespace",
|
||||
Class = "TestClass",
|
||||
Method = "TestMethod",
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), documentNode);
|
||||
|
||||
// Assert
|
||||
var @namespace = SingleChild<NamespaceDeclarationIntermediateNode>(documentNode);
|
||||
Assert.Equal("TestNamespace", @namespace.Content);
|
||||
|
||||
var @class = SingleChild<ClassDeclarationIntermediateNode>(@namespace);
|
||||
Assert.Equal("TestClass", @class.ClassName);
|
||||
|
||||
var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
|
||||
Assert.Equal("TestMethod", method.MethodName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_AddsPrimaryAnnotations()
|
||||
{
|
||||
// Arrange
|
||||
var documentNode = new DocumentIntermediateNode()
|
||||
{
|
||||
Options = RazorCodeGenerationOptions.CreateDefault(),
|
||||
};
|
||||
|
||||
var builder = IntermediateNodeBuilder.Create(documentNode);
|
||||
builder.Add(new HtmlContentIntermediateNode());
|
||||
builder.Add(new CSharpCodeIntermediateNode());
|
||||
|
||||
var pass = new TestDocumentClassifierPass()
|
||||
{
|
||||
Engine = Engine,
|
||||
Namespace = "TestNamespace",
|
||||
Class = "TestClass",
|
||||
Method = "TestMethod",
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), documentNode);
|
||||
|
||||
// Assert
|
||||
var @namespace = SingleChild<NamespaceDeclarationIntermediateNode>(documentNode);
|
||||
AnnotationEquals(@namespace, CommonAnnotations.PrimaryNamespace);
|
||||
|
||||
var @class = SingleChild<ClassDeclarationIntermediateNode>(@namespace);
|
||||
AnnotationEquals(@class, CommonAnnotations.PrimaryClass);
|
||||
|
||||
var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
|
||||
AnnotationEquals(method, CommonAnnotations.PrimaryMethod);
|
||||
}
|
||||
|
||||
private class TestDocumentClassifierPass : DocumentClassifierPassBase
|
||||
{
|
||||
public override int Order => IntermediateNodePassBase.DefaultFeatureOrder;
|
||||
|
||||
public bool ShouldMatch { get; set; } = true;
|
||||
|
||||
public Action<CodeTargetBuilder> CodeTargetCallback { get; set; }
|
||||
|
||||
public string Namespace { get; set; }
|
||||
|
||||
public string Class { get; set; }
|
||||
|
||||
public string Method { get; set; }
|
||||
|
||||
protected override string DocumentKind => "test";
|
||||
|
||||
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
return ShouldMatch;
|
||||
}
|
||||
|
||||
protected override void OnDocumentStructureCreated(
|
||||
RazorCodeDocument codeDocument,
|
||||
NamespaceDeclarationIntermediateNode @namespace,
|
||||
ClassDeclarationIntermediateNode @class,
|
||||
MethodDeclarationIntermediateNode method)
|
||||
{
|
||||
@namespace.Content = Namespace;
|
||||
@class.ClassName = Class;
|
||||
@method.MethodName = Method;
|
||||
}
|
||||
|
||||
protected override void ConfigureTarget(CodeTargetBuilder builder)
|
||||
{
|
||||
CodeTargetCallback?.Invoke(builder);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyExtension1 : ICodeTargetExtension
|
||||
{
|
||||
}
|
||||
|
||||
private class MyExtension2 : ICodeTargetExtension
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
public class DefaultMetadataIdentifierFeatureTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Latest;
|
||||
|
||||
[Fact]
|
||||
public void GetIdentifier_ReturnsNull_ForNullRelativePath()
|
||||
{
|
||||
// Arrange
|
||||
var sourceDocument = RazorSourceDocument.Create("content", new RazorSourceDocumentProperties("Test.cshtml", null));
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var feature = new DefaultMetadataIdentifierFeature()
|
||||
{
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = feature.GetIdentifier(codeDocument, sourceDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetIdentifier_ReturnsNull_ForEmptyRelativePath()
|
||||
{
|
||||
// Arrange
|
||||
var sourceDocument = RazorSourceDocument.Create("content", new RazorSourceDocumentProperties("Test.cshtml", string.Empty));
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var feature = new DefaultMetadataIdentifierFeature()
|
||||
{
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = feature.GetIdentifier(codeDocument, sourceDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Test.cshtml", "/Test.cshtml")]
|
||||
[InlineData("/Test.cshtml", "/Test.cshtml")]
|
||||
[InlineData("\\Test.cshtml", "/Test.cshtml")]
|
||||
[InlineData("\\About\\Test.cshtml", "/About/Test.cshtml")]
|
||||
[InlineData("\\About\\Test\\cshtml", "/About/Test/cshtml")]
|
||||
public void GetIdentifier_SanitizesRelativePath(string relativePath, string expected)
|
||||
{
|
||||
// Arrange
|
||||
var sourceDocument = RazorSourceDocument.Create("content", new RazorSourceDocumentProperties("Test.cshtml", relativePath));
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var feature = new DefaultMetadataIdentifierFeature()
|
||||
{
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = feature.GetIdentifier(codeDocument, sourceDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
public class DefaultTagHelperOptimizationPassTest
|
||||
{
|
||||
[Fact]
|
||||
public void DefaultTagHelperOptimizationPass_Execute_ReplacesChildren()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = CreateDocument(@"
|
||||
@addTagHelper TestTagHelper, TestAssembly
|
||||
<p foo=""17"" attr=""value"">");
|
||||
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly")
|
||||
.TypeName("TestTagHelper")
|
||||
.BoundAttributeDescriptor(attribute => attribute
|
||||
.Name("Foo")
|
||||
.TypeName("System.Int32")
|
||||
.PropertyName("FooProp"))
|
||||
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
|
||||
.Build()
|
||||
};
|
||||
|
||||
var engine = CreateEngine(tagHelpers);
|
||||
var pass = new DefaultTagHelperOptimizationPass()
|
||||
{
|
||||
Engine = engine
|
||||
};
|
||||
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
var @class = irDocument.FindPrimaryClass();
|
||||
Assert.IsType<DefaultTagHelperRuntimeIntermediateNode>(@class.Children[0]);
|
||||
|
||||
var fieldDeclaration = Assert.IsType<FieldDeclarationIntermediateNode>(@class.Children[1]);
|
||||
Assert.Equal(bool.TrueString, fieldDeclaration.Annotations[CommonAnnotations.DefaultTagHelperExtension.TagHelperField]);
|
||||
Assert.Equal("__TestTagHelper", fieldDeclaration.FieldName);
|
||||
Assert.Equal("global::TestTagHelper", fieldDeclaration.FieldType);
|
||||
Assert.Equal("private", fieldDeclaration.Modifiers.First());
|
||||
|
||||
var tagHelper = FindTagHelperNode(irDocument);
|
||||
Assert.Equal(5, tagHelper.Children.Count);
|
||||
|
||||
var body = Assert.IsType<DefaultTagHelperBodyIntermediateNode>(tagHelper.Children[0]);
|
||||
Assert.Equal("p", body.TagName);
|
||||
Assert.Equal(TagMode.StartTagAndEndTag, body.TagMode);
|
||||
|
||||
var create = Assert.IsType<DefaultTagHelperCreateIntermediateNode>(tagHelper.Children[1]);
|
||||
Assert.Equal("__TestTagHelper", create.FieldName);
|
||||
Assert.Equal("TestTagHelper", create.TypeName);
|
||||
Assert.Equal(tagHelpers[0], create.TagHelper, TagHelperDescriptorComparer.CaseSensitive);
|
||||
|
||||
var property = Assert.IsType<DefaultTagHelperPropertyIntermediateNode>(tagHelper.Children[2]);
|
||||
Assert.Equal("foo", property.AttributeName);
|
||||
Assert.Equal(AttributeStructure.DoubleQuotes, property.AttributeStructure);
|
||||
Assert.Equal(tagHelpers[0].BoundAttributes[0], property.BoundAttribute, BoundAttributeDescriptorComparer.CaseSensitive);
|
||||
Assert.Equal("__TestTagHelper", property.FieldName);
|
||||
Assert.False(property.IsIndexerNameMatch);
|
||||
Assert.Equal("FooProp", property.PropertyName);
|
||||
Assert.Equal(tagHelpers[0], property.TagHelper, TagHelperDescriptorComparer.CaseSensitive);
|
||||
|
||||
var htmlAttribute = Assert.IsType<DefaultTagHelperHtmlAttributeIntermediateNode>(tagHelper.Children[3]);
|
||||
Assert.Equal("attr", htmlAttribute.AttributeName);
|
||||
Assert.Equal(AttributeStructure.DoubleQuotes, htmlAttribute.AttributeStructure);
|
||||
|
||||
Assert.IsType<DefaultTagHelperExecuteIntermediateNode>(tagHelper.Children[4]);
|
||||
}
|
||||
|
||||
private RazorCodeDocument CreateDocument(string content)
|
||||
{
|
||||
var source = RazorSourceDocument.Create(content, "test.cshtml");
|
||||
return RazorCodeDocument.Create(source);
|
||||
}
|
||||
|
||||
private RazorEngine CreateEngine(params TagHelperDescriptor[] tagHelpers)
|
||||
{
|
||||
return RazorProjectEngine.Create(b =>
|
||||
{
|
||||
b.Features.Add(new TestTagHelperFeature(tagHelpers));
|
||||
}).Engine;
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode CreateIRDocument(RazorEngine engine, RazorCodeDocument codeDocument)
|
||||
{
|
||||
for (var i = 0; i < engine.Phases.Count; i++)
|
||||
{
|
||||
var phase = engine.Phases[i];
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
if (phase is IRazorDirectiveClassifierPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return codeDocument.GetDocumentIntermediateNode();
|
||||
}
|
||||
|
||||
private TagHelperIntermediateNode FindTagHelperNode(IntermediateNode node)
|
||||
{
|
||||
var visitor = new TagHelperNodeVisitor();
|
||||
visitor.Visit(node);
|
||||
return visitor.Node;
|
||||
}
|
||||
|
||||
private class TagHelperNodeVisitor : IntermediateNodeWalker
|
||||
{
|
||||
public TagHelperIntermediateNode Node { get; set; }
|
||||
|
||||
public override void VisitTagHelper(TagHelperIntermediateNode node)
|
||||
{
|
||||
Node = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,247 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
public class DesignTimeDirectiveTargetExtensionTest
|
||||
{
|
||||
[Fact]
|
||||
public void WriteDesignTimeDirective_NoChildren_WritesEmptyMethod_WithPragma()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new DesignTimeDirectiveTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new DesignTimeDirectiveIntermediateNode();
|
||||
|
||||
// Act
|
||||
extension.WriteDesignTimeDirective(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"#pragma warning disable 219
|
||||
private void __RazorDirectiveTokenHelpers__() {
|
||||
}
|
||||
#pragma warning restore 219
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDesignTimeDirective_WithTypeToken_WritesLambda()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new DesignTimeDirectiveTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new DesignTimeDirectiveIntermediateNode();
|
||||
var token = new DirectiveTokenIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 5),
|
||||
Content = "System.String",
|
||||
DirectiveToken = DirectiveTokenDescriptor.CreateToken(DirectiveTokenKind.Type),
|
||||
};
|
||||
node.Children.Add(token);
|
||||
|
||||
// Act
|
||||
extension.WriteDesignTimeDirective(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"#pragma warning disable 219
|
||||
private void __RazorDirectiveTokenHelpers__() {
|
||||
((System.Action)(() => {
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
System.String __typeHelper = default(System.String);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
}
|
||||
))();
|
||||
}
|
||||
#pragma warning restore 219
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDesignTimeDirective_WithNamespaceToken_WritesLambda()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new DesignTimeDirectiveTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new DesignTimeDirectiveIntermediateNode();
|
||||
var token = new DirectiveTokenIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 5),
|
||||
Content = "System.Collections.Generic",
|
||||
DirectiveToken = DirectiveTokenDescriptor.CreateToken(DirectiveTokenKind.Namespace),
|
||||
};
|
||||
node.Children.Add(token);
|
||||
|
||||
// Act
|
||||
extension.WriteDesignTimeDirective(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"#pragma warning disable 219
|
||||
private void __RazorDirectiveTokenHelpers__() {
|
||||
((System.Action)(() => {
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
global::System.Object __typeHelper = nameof(System.Collections.Generic);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
}
|
||||
))();
|
||||
}
|
||||
#pragma warning restore 219
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDesignTimeDirective_WithMemberToken_WritesLambda()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new DesignTimeDirectiveTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new DesignTimeDirectiveIntermediateNode();
|
||||
var token = new DirectiveTokenIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 5),
|
||||
Content = "Foo",
|
||||
DirectiveToken = DirectiveTokenDescriptor.CreateToken(DirectiveTokenKind.Member),
|
||||
};
|
||||
node.Children.Add(token);
|
||||
|
||||
// Act
|
||||
extension.WriteDesignTimeDirective(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"#pragma warning disable 219
|
||||
private void __RazorDirectiveTokenHelpers__() {
|
||||
((System.Action)(() => {
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
global::System.Object Foo = null;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
}
|
||||
))();
|
||||
}
|
||||
#pragma warning restore 219
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDesignTimeDirective_WithStringToken_WritesLambda()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new DesignTimeDirectiveTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new DesignTimeDirectiveIntermediateNode();
|
||||
var token = new DirectiveTokenIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 5),
|
||||
Content = "Value",
|
||||
DirectiveToken = DirectiveTokenDescriptor.CreateToken(DirectiveTokenKind.String),
|
||||
};
|
||||
var tokenWithQuotedContent = new DirectiveTokenIntermediateNode()
|
||||
{
|
||||
Source = new SourceSpan("test.cshtml", 0, 0, 0, 5),
|
||||
Content = "\"Value\"",
|
||||
DirectiveToken = DirectiveTokenDescriptor.CreateToken(DirectiveTokenKind.String),
|
||||
};
|
||||
node.Children.Add(token);
|
||||
node.Children.Add(tokenWithQuotedContent);
|
||||
|
||||
// Act
|
||||
extension.WriteDesignTimeDirective(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"#pragma warning disable 219
|
||||
private void __RazorDirectiveTokenHelpers__() {
|
||||
((System.Action)(() => {
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
global::System.Object __typeHelper = ""Value"";
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
}
|
||||
))();
|
||||
((System.Action)(() => {
|
||||
#nullable restore
|
||||
#line 1 ""test.cshtml""
|
||||
global::System.Object __typeHelper = ""Value"";
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
}
|
||||
))();
|
||||
}
|
||||
#pragma warning restore 219
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDesignTimeDirective_ChildrenWithNoSource_WritesEmptyMethod_WithPragma()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new DesignTimeDirectiveTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var node = new DesignTimeDirectiveIntermediateNode();
|
||||
var token = new DirectiveTokenIntermediateNode()
|
||||
{
|
||||
Content = "Value",
|
||||
DirectiveToken = DirectiveTokenDescriptor.CreateToken(DirectiveTokenKind.String),
|
||||
};
|
||||
node.Children.Add(token);
|
||||
|
||||
// Act
|
||||
extension.WriteDesignTimeDirective(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"#pragma warning disable 219
|
||||
private void __RazorDirectiveTokenHelpers__() {
|
||||
}
|
||||
#pragma warning restore 219
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
using static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeAssert;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
public class FunctionsDirectivePassTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Latest;
|
||||
|
||||
[Fact]
|
||||
public void Execute_SkipsDocumentWithNoClassNode()
|
||||
{
|
||||
// Arrange
|
||||
var projectEngine = CreateProjectEngine();
|
||||
var pass = new FunctionsDirectivePass()
|
||||
{
|
||||
Engine = projectEngine.Engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create("@functions { var value = true; }");
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = new DocumentIntermediateNode();
|
||||
irDocument.Children.Add(new DirectiveIntermediateNode() { Directive = FunctionsDirective.Directive, });
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
irDocument,
|
||||
node => Assert.IsType<DirectiveIntermediateNode>(node));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_AddsStatementsToClassLevel()
|
||||
{
|
||||
// Arrange
|
||||
var projectEngine = CreateProjectEngine();
|
||||
var pass = new FunctionsDirectivePass()
|
||||
{
|
||||
Engine = projectEngine.Engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create("@functions { var value = true; }");
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = Lower(codeDocument, projectEngine);
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
irDocument,
|
||||
node => Assert.IsType<NamespaceDeclarationIntermediateNode>(node));
|
||||
|
||||
var @namespace = irDocument.Children[0];
|
||||
Children(
|
||||
@namespace,
|
||||
node => Assert.IsType<ClassDeclarationIntermediateNode>(node));
|
||||
|
||||
var @class = @namespace.Children[0];
|
||||
Children(
|
||||
@class,
|
||||
node => Assert.IsType<MethodDeclarationIntermediateNode>(node),
|
||||
node => CSharpCode(" var value = true; ", node));
|
||||
|
||||
var method = @class.Children[0];
|
||||
Children(
|
||||
method,
|
||||
node => Assert.IsType<DirectiveIntermediateNode>(node));
|
||||
}
|
||||
|
||||
private static DocumentIntermediateNode Lower(RazorCodeDocument codeDocument, RazorProjectEngine projectEngine)
|
||||
{
|
||||
for (var i = 0; i < projectEngine.Phases.Count; i++)
|
||||
{
|
||||
var phase = projectEngine.Phases[i];
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
if (phase is IRazorDocumentClassifierPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var irDocument = codeDocument.GetDocumentIntermediateNode();
|
||||
Assert.NotNull(irDocument);
|
||||
|
||||
return irDocument;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
using static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeAssert;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
public class InheritsDirectivePassTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Latest;
|
||||
|
||||
[Fact]
|
||||
public void Execute_SkipsDocumentWithNoClassNode()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateEngine();
|
||||
var pass = new InheritsDirectivePass()
|
||||
{
|
||||
Engine = engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create("@inherits Hello<World[]>");
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = new DocumentIntermediateNode();
|
||||
irDocument.Children.Add(new DirectiveIntermediateNode() { Directive = FunctionsDirective.Directive, });
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
irDocument,
|
||||
node => Assert.IsType<DirectiveIntermediateNode>(node));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_Inherits_SetsClassDeclarationBaseType()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateEngine();
|
||||
var pass = new InheritsDirectivePass()
|
||||
{
|
||||
Engine = engine,
|
||||
};
|
||||
|
||||
var content = "@inherits Hello<World[]>";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = Lower(codeDocument, engine);
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
irDocument,
|
||||
node => Assert.IsType<NamespaceDeclarationIntermediateNode>(node));
|
||||
|
||||
var @namespace = irDocument.Children[0];
|
||||
Children(
|
||||
@namespace,
|
||||
node => Assert.IsType<ClassDeclarationIntermediateNode>(node));
|
||||
|
||||
var @class = (ClassDeclarationIntermediateNode)@namespace.Children[0];
|
||||
Assert.Equal("Hello<World[]>", @class.BaseType);
|
||||
}
|
||||
|
||||
private static DocumentIntermediateNode Lower(RazorCodeDocument codeDocument, RazorEngine engine)
|
||||
{
|
||||
for (var i = 0; i < engine.Phases.Count; i++)
|
||||
{
|
||||
var phase = engine.Phases[i];
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
if (phase is IRazorDocumentClassifierPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var irDocument = codeDocument.GetDocumentIntermediateNode();
|
||||
Assert.NotNull(irDocument);
|
||||
|
||||
return irDocument;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,392 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Components;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
using static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeAssert;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
public class MetadataAttributePassTest
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_NullCodeGenerationOptions_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateEngine();
|
||||
var pass = new MetadataAttributePass()
|
||||
{
|
||||
Engine = engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create();
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = new DocumentIntermediateNode();
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
NoChildren(irDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_SuppressMetadataAttributes_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateEngine();
|
||||
var pass = new MetadataAttributePass()
|
||||
{
|
||||
Engine = engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create();
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = new DocumentIntermediateNode()
|
||||
{
|
||||
Options = RazorCodeGenerationOptions.Create(o =>
|
||||
{
|
||||
o.SuppressMetadataAttributes = true;
|
||||
}),
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
NoChildren(irDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ComponentDocumentKind_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateEngine();
|
||||
var pass = new MetadataAttributePass()
|
||||
{
|
||||
Engine = engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create();
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = new DocumentIntermediateNode()
|
||||
{
|
||||
DocumentKind = ComponentDocumentClassifierPass.ComponentDocumentKind,
|
||||
Options = RazorCodeGenerationOptions.Create(o =>
|
||||
{
|
||||
o.SuppressMetadataAttributes = true;
|
||||
}),
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
NoChildren(irDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_NoNamespaceSet_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateEngine();
|
||||
var pass = new MetadataAttributePass()
|
||||
{
|
||||
Engine = engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create();
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = new DocumentIntermediateNode()
|
||||
{
|
||||
DocumentKind = "test",
|
||||
Options = RazorCodeGenerationOptions.Create((o) => { }),
|
||||
};
|
||||
var builder = IntermediateNodeBuilder.Create(irDocument);
|
||||
var @namespace = new NamespaceDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace,
|
||||
},
|
||||
};
|
||||
builder.Push(@namespace);
|
||||
var @class = new ClassDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass,
|
||||
},
|
||||
ClassName = "Test",
|
||||
};
|
||||
builder.Add(@class);
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
SingleChild<NamespaceDeclarationIntermediateNode>(irDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_NoClassNameSet_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateEngine();
|
||||
var pass = new MetadataAttributePass()
|
||||
{
|
||||
Engine = engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create();
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = new DocumentIntermediateNode()
|
||||
{
|
||||
DocumentKind = "test",
|
||||
Options = RazorCodeGenerationOptions.Create((o) => { }),
|
||||
};
|
||||
var builder = IntermediateNodeBuilder.Create(irDocument);
|
||||
var @namespace = new NamespaceDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace,
|
||||
},
|
||||
Content = "Some.Namespace"
|
||||
};
|
||||
builder.Push(@namespace);
|
||||
var @class = new ClassDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass,
|
||||
},
|
||||
};
|
||||
builder.Add(@class);
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
SingleChild<NamespaceDeclarationIntermediateNode>(irDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_NoDocumentKind_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateEngine();
|
||||
var pass = new MetadataAttributePass()
|
||||
{
|
||||
Engine = engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create();
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = new DocumentIntermediateNode();
|
||||
var builder = IntermediateNodeBuilder.Create(irDocument);
|
||||
var @namespace = new NamespaceDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace,
|
||||
},
|
||||
Content = "Some.Namespace"
|
||||
};
|
||||
builder.Push(@namespace);
|
||||
var @class = new ClassDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass,
|
||||
},
|
||||
ClassName = "Test",
|
||||
};
|
||||
builder.Add(@class);
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
SingleChild<NamespaceDeclarationIntermediateNode>(irDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_NoIdentifier_Noops()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateEngine();
|
||||
var pass = new MetadataAttributePass()
|
||||
{
|
||||
Engine = engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create("", new RazorSourceDocumentProperties(null, null));
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = new DocumentIntermediateNode()
|
||||
{
|
||||
DocumentKind = "test",
|
||||
Options = RazorCodeGenerationOptions.Create((o) => { }),
|
||||
};
|
||||
var builder = IntermediateNodeBuilder.Create(irDocument);
|
||||
var @namespace = new NamespaceDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace,
|
||||
},
|
||||
Content = "Some.Namespace"
|
||||
};
|
||||
builder.Push(@namespace);
|
||||
var @class = new ClassDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass,
|
||||
},
|
||||
ClassName = "Test",
|
||||
};
|
||||
builder.Add(@class);
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
SingleChild<NamespaceDeclarationIntermediateNode>(irDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_HasRequiredInfo_AddsItemAndSourceChecksum()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateEngine();
|
||||
var pass = new MetadataAttributePass()
|
||||
{
|
||||
Engine = engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create("", new RazorSourceDocumentProperties(null, "Foo\\Bar.cshtml"));
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = new DocumentIntermediateNode()
|
||||
{
|
||||
DocumentKind = "test",
|
||||
Options = RazorCodeGenerationOptions.Create((o) => { }),
|
||||
};
|
||||
var builder = IntermediateNodeBuilder.Create(irDocument);
|
||||
var @namespace = new NamespaceDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace,
|
||||
},
|
||||
Content = "Some.Namespace"
|
||||
};
|
||||
builder.Push(@namespace);
|
||||
var @class = new ClassDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass,
|
||||
},
|
||||
ClassName = "Test",
|
||||
};
|
||||
builder.Add(@class);
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, irDocument.Children.Count);
|
||||
|
||||
var item = Assert.IsType<RazorCompiledItemAttributeIntermediateNode>(irDocument.Children[0]);
|
||||
Assert.Equal("/Foo/Bar.cshtml", item.Identifier);
|
||||
Assert.Equal("test", item.Kind);
|
||||
Assert.Equal("Some.Namespace.Test", item.TypeName);
|
||||
|
||||
Assert.Equal(2, @namespace.Children.Count);
|
||||
var checksum = Assert.IsType<RazorSourceChecksumAttributeIntermediateNode>(@namespace.Children[0]);
|
||||
Assert.NotNull(checksum.Checksum); // Not verifying the checksum here
|
||||
Assert.Equal("SHA1", checksum.ChecksumAlgorithm);
|
||||
Assert.Equal("/Foo/Bar.cshtml", checksum.Identifier);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_HasRequiredInfo_AndImport_AddsItemAndSourceChecksum()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateEngine();
|
||||
var pass = new MetadataAttributePass()
|
||||
{
|
||||
Engine = engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create("", new RazorSourceDocumentProperties(null, "Foo\\Bar.cshtml"));
|
||||
var import = TestRazorSourceDocument.Create("@using System", new RazorSourceDocumentProperties(null, "Foo\\Import.cshtml"));
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument, new[] { import, });
|
||||
|
||||
var irDocument = new DocumentIntermediateNode()
|
||||
{
|
||||
DocumentKind = "test",
|
||||
Options = RazorCodeGenerationOptions.Create((o) => { }),
|
||||
};
|
||||
var builder = IntermediateNodeBuilder.Create(irDocument);
|
||||
var @namespace = new NamespaceDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace,
|
||||
},
|
||||
Content = "Some.Namespace"
|
||||
};
|
||||
builder.Push(@namespace);
|
||||
var @class = new ClassDeclarationIntermediateNode
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass,
|
||||
},
|
||||
ClassName = "Test",
|
||||
};
|
||||
builder.Add(@class);
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, irDocument.Children.Count);
|
||||
|
||||
var item = Assert.IsType<RazorCompiledItemAttributeIntermediateNode>(irDocument.Children[0]);
|
||||
Assert.Equal("/Foo/Bar.cshtml", item.Identifier);
|
||||
Assert.Equal("test", item.Kind);
|
||||
Assert.Equal("Some.Namespace.Test", item.TypeName);
|
||||
|
||||
Assert.Equal(3, @namespace.Children.Count);
|
||||
var checksum = Assert.IsType<RazorSourceChecksumAttributeIntermediateNode>(@namespace.Children[0]);
|
||||
Assert.NotNull(checksum.Checksum); // Not verifying the checksum here
|
||||
Assert.Equal("SHA1", checksum.ChecksumAlgorithm);
|
||||
Assert.Equal("/Foo/Bar.cshtml", checksum.Identifier);
|
||||
|
||||
checksum = Assert.IsType<RazorSourceChecksumAttributeIntermediateNode>(@namespace.Children[1]);
|
||||
Assert.NotNull(checksum.Checksum); // Not verifying the checksum here
|
||||
Assert.Equal("SHA1", checksum.ChecksumAlgorithm);
|
||||
Assert.Equal("/Foo/Import.cshtml", checksum.Identifier);
|
||||
}
|
||||
|
||||
private static RazorEngine CreateEngine()
|
||||
{
|
||||
return RazorProjectEngine.Create(b =>
|
||||
{
|
||||
b.Features.Add(new DefaultMetadataIdentifierFeature());
|
||||
}).Engine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
public class MetadataAttributeTargetExtensionTest
|
||||
{
|
||||
[Fact]
|
||||
public void WriteRazorCompiledItemAttribute_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new MetadataAttributeTargetExtension()
|
||||
{
|
||||
CompiledItemAttributeName = "global::TestItem",
|
||||
};
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new RazorCompiledItemAttributeIntermediateNode()
|
||||
{
|
||||
TypeName = "Foo.Bar",
|
||||
Kind = "test",
|
||||
Identifier = "Foo/Bar",
|
||||
};
|
||||
|
||||
// Act
|
||||
extension.WriteRazorCompiledItemAttribute(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"[assembly: global::TestItem(typeof(Foo.Bar), @""test"", @""Foo/Bar"")]
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRazorSourceChecksumAttribute_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new MetadataAttributeTargetExtension()
|
||||
{
|
||||
SourceChecksumAttributeName = "global::TestChecksum",
|
||||
};
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new RazorSourceChecksumAttributeIntermediateNode()
|
||||
{
|
||||
ChecksumAlgorithm = "SHA1",
|
||||
Checksum = new byte[] { (byte)'t', (byte)'e', (byte)'s', (byte)'t', },
|
||||
Identifier = "Foo/Bar",
|
||||
};
|
||||
|
||||
// Act
|
||||
extension.WriteRazorSourceChecksumAttribute(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"[global::TestChecksum(@""SHA1"", @""74657374"", @""Foo/Bar"")]
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRazorCompiledItemAttributeMetadata_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new MetadataAttributeTargetExtension()
|
||||
{
|
||||
CompiledItemMetadataAttributeName = "global::TestItemMetadata",
|
||||
};
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new RazorCompiledItemMetadataAttributeIntermediateNode
|
||||
{
|
||||
Key = "key",
|
||||
Value = "value",
|
||||
};
|
||||
|
||||
// Act
|
||||
extension.WriteRazorCompiledItemMetadataAttribute(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode().Trim();
|
||||
Assert.Equal(
|
||||
"[global::TestItemMetadata(\"key\", \"value\")]",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRazorCompiledItemAttributeMetadata_EscapesKeysAndValuesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new MetadataAttributeTargetExtension()
|
||||
{
|
||||
CompiledItemMetadataAttributeName = "global::TestItemMetadata",
|
||||
};
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new RazorCompiledItemMetadataAttributeIntermediateNode
|
||||
{
|
||||
Key = "\"test\" key",
|
||||
Value = @"""test"" value",
|
||||
};
|
||||
|
||||
// Act
|
||||
extension.WriteRazorCompiledItemMetadataAttribute(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode().Trim();
|
||||
Assert.Equal(
|
||||
"[global::TestItemMetadata(\"\\\"test\\\" key\", \"\\\"test\\\" value\")]",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
public class PreallocatedAttributeTargetExtensionTest
|
||||
{
|
||||
[Fact]
|
||||
public void WriteTagHelperHtmlAttributeValue_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new PreallocatedTagHelperHtmlAttributeValueIntermediateNode()
|
||||
{
|
||||
AttributeName = "Foo",
|
||||
Value = "Bar",
|
||||
AttributeStructure = AttributeStructure.DoubleQuotes,
|
||||
VariableName = "MyProp"
|
||||
};
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperHtmlAttributeValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"private static readonly global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute MyProp = new global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute(""Foo"", new global::Microsoft.AspNetCore.Html.HtmlString(""Bar""), global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.DoubleQuotes);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTagHelperHtmlAttributeValue_Minimized_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new PreallocatedTagHelperHtmlAttributeValueIntermediateNode()
|
||||
{
|
||||
AttributeName = "Foo",
|
||||
Value = "Bar",
|
||||
AttributeStructure = AttributeStructure.Minimized,
|
||||
VariableName = "_tagHelper1"
|
||||
};
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperHtmlAttributeValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"private static readonly global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute _tagHelper1 = new global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute(""Foo"");
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTagHelperHtmlAttribute_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new PreallocatedTagHelperHtmlAttributeIntermediateNode()
|
||||
{
|
||||
VariableName = "_tagHelper1"
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperHtmlAttribute(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"__tagHelperExecutionContext.AddHtmlAttribute(_tagHelper1);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTagHelperPropertyValue_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var node = new PreallocatedTagHelperPropertyValueIntermediateNode()
|
||||
{
|
||||
AttributeName = "Foo",
|
||||
Value = "Bar",
|
||||
AttributeStructure = AttributeStructure.DoubleQuotes,
|
||||
VariableName = "_tagHelper1",
|
||||
};
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperPropertyValue(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"private static readonly global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute _tagHelper1 = new global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute(""Foo"", ""Bar"", global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.DoubleQuotes);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTagHelperProperty_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "FooTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("FooTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("Foo")
|
||||
.TypeName("System.String")
|
||||
.PropertyName("FooProp");
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new PreallocatedTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = descriptor.Name,
|
||||
BoundAttribute = descriptor,
|
||||
FieldName = "__FooTagHelper",
|
||||
PropertyName = "FooProp",
|
||||
VariableName = "_tagHelper1",
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"__FooTagHelper.FooProp = (string)_tagHelper1.Value;
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute(_tagHelper1);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSetPreallocatedTagHelperProperty_IndexerAttribute_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "FooTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("FooTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("Foo")
|
||||
.TypeName("System.Collections.Generic.Dictionary<System.String, System.String>")
|
||||
.AsDictionaryAttribute("pre-", "System.String")
|
||||
.PropertyName("FooProp");
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new PreallocatedTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "pre-Foo",
|
||||
FieldName = "__FooTagHelper",
|
||||
VariableName = "_tagHelper1",
|
||||
BoundAttribute = descriptor,
|
||||
IsIndexerNameMatch = true,
|
||||
PropertyName = "FooProp",
|
||||
TagHelper = tagHelperBuilder.Build(),
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"if (__FooTagHelper.FooProp == null)
|
||||
{
|
||||
throw new InvalidOperationException(InvalidTagHelperIndexerAssignment(""pre-Foo"", ""FooTagHelper"", ""FooProp""));
|
||||
}
|
||||
__FooTagHelper.FooProp[""Foo""] = (string)_tagHelper1.Value;
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute(_tagHelper1);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSetPreallocatedTagHelperProperty_IndexerAttribute_MultipleValues()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "FooTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("FooTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("Foo")
|
||||
.TypeName("System.Collections.Generic.Dictionary<System.String, System.String>")
|
||||
.AsDictionaryAttribute("pre-", "System.String")
|
||||
.PropertyName("FooProp");
|
||||
|
||||
var boundAttribute = builder.Build();
|
||||
var tagHelper = tagHelperBuilder.Build();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node1 = new PreallocatedTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "pre-Bar",
|
||||
FieldName = "__FooTagHelper",
|
||||
VariableName = "_tagHelper0s",
|
||||
BoundAttribute = boundAttribute,
|
||||
IsIndexerNameMatch = true,
|
||||
PropertyName = "FooProp",
|
||||
TagHelper = tagHelper,
|
||||
};
|
||||
var node2 = new PreallocatedTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "pre-Foo",
|
||||
FieldName = "__FooTagHelper",
|
||||
VariableName = "_tagHelper1",
|
||||
BoundAttribute = boundAttribute,
|
||||
IsIndexerNameMatch = true,
|
||||
PropertyName = "FooProp",
|
||||
TagHelper = tagHelper,
|
||||
};
|
||||
tagHelperNode.Children.Add(node1);
|
||||
tagHelperNode.Children.Add(node2);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node2);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(
|
||||
@"__FooTagHelper.FooProp[""Foo""] = (string)_tagHelper1.Value;
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute(_tagHelper1);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
private static void Push(CodeRenderingContext context, TagHelperIntermediateNode node)
|
||||
{
|
||||
((DefaultCodeRenderingContext)context).AncestorsInternal.Push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
using static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeAssert;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
public class SectionDirectivePassTest
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_SkipsDocumentWithNoClassNode()
|
||||
{
|
||||
// Arrange
|
||||
var projectEngine = CreateProjectEngine();
|
||||
var pass = new SectionDirectivePass()
|
||||
{
|
||||
Engine = projectEngine.Engine,
|
||||
};
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create("@section Header { <p>Hello World</p> }");
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = new DocumentIntermediateNode();
|
||||
irDocument.Children.Add(new DirectiveIntermediateNode() { Directive = SectionDirective.Directive, });
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
irDocument,
|
||||
node => Assert.IsType<DirectiveIntermediateNode>(node));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_WrapsStatementInSectionNode()
|
||||
{
|
||||
// Arrange
|
||||
var projectEngine = CreateProjectEngine();
|
||||
var pass = new SectionDirectivePass()
|
||||
{
|
||||
Engine = projectEngine.Engine,
|
||||
};
|
||||
|
||||
var content = "@section Header { <p>Hello World</p> }";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
var irDocument = Lower(codeDocument, projectEngine);
|
||||
|
||||
// Act
|
||||
pass.Execute(codeDocument, irDocument);
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
irDocument,
|
||||
node => Assert.IsType<NamespaceDeclarationIntermediateNode>(node));
|
||||
|
||||
var @namespace = irDocument.Children[0];
|
||||
Children(
|
||||
@namespace,
|
||||
node => Assert.IsType<ClassDeclarationIntermediateNode>(node));
|
||||
|
||||
var @class = @namespace.Children[0];
|
||||
var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
|
||||
|
||||
Children(
|
||||
method,
|
||||
node => Assert.IsType<DirectiveIntermediateNode>(node),
|
||||
node => Assert.IsType<SectionIntermediateNode>(node));
|
||||
|
||||
var section = method.Children[1] as SectionIntermediateNode;
|
||||
Assert.Equal("Header", section.SectionName);
|
||||
Children(section, c => Html(" <p>Hello World</p> ", c));
|
||||
}
|
||||
|
||||
private static RazorProjectEngine CreateProjectEngine()
|
||||
{
|
||||
return RazorProjectEngine.Create(b =>
|
||||
{
|
||||
SectionDirective.Register(b);
|
||||
});
|
||||
}
|
||||
|
||||
private static DocumentIntermediateNode Lower(RazorCodeDocument codeDocument, RazorProjectEngine projectEngine)
|
||||
{
|
||||
for (var i = 0; i < projectEngine.Phases.Count; i++)
|
||||
{
|
||||
var phase = projectEngine.Phases[i];
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
if (phase is IRazorDocumentClassifierPhase)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var irDocument = codeDocument.GetDocumentIntermediateNode();
|
||||
Assert.NotNull(irDocument);
|
||||
|
||||
return irDocument;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
public class SectionTargetExtensionTest
|
||||
{
|
||||
[Fact]
|
||||
public void WriteSection_WritesSectionCode()
|
||||
{
|
||||
// Arrange
|
||||
var node = new SectionIntermediateNode()
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new CSharpExpressionIntermediateNode(),
|
||||
},
|
||||
SectionName = "MySection"
|
||||
};
|
||||
|
||||
var extension = new SectionTargetExtension()
|
||||
{
|
||||
SectionMethodName = "CreateSection"
|
||||
};
|
||||
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
// Act
|
||||
extension.WriteSection(context, node);
|
||||
|
||||
// Assert
|
||||
var expected = @"CreateSection(""MySection"", async() => {
|
||||
Render Children
|
||||
}
|
||||
);
|
||||
";
|
||||
|
||||
var output = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(expected, output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSection_WritesSectionCode_DesignTime()
|
||||
{
|
||||
// Arrange
|
||||
var node = new SectionIntermediateNode()
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new CSharpExpressionIntermediateNode(),
|
||||
},
|
||||
SectionName = "MySection"
|
||||
};
|
||||
|
||||
var extension = new SectionTargetExtension()
|
||||
{
|
||||
SectionMethodName = "CreateSection"
|
||||
};
|
||||
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
// Act
|
||||
extension.WriteSection(context, node);
|
||||
|
||||
// Assert
|
||||
var expected = @"CreateSection(""MySection"", async(__razor_section_writer) => {
|
||||
Render Children
|
||||
}
|
||||
);
|
||||
";
|
||||
|
||||
var output = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(expected, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
public class TemplateTargetExtensionTest
|
||||
{
|
||||
[Fact]
|
||||
public void WriteTemplate_WritesTemplateCode()
|
||||
{
|
||||
// Arrange
|
||||
var node = new TemplateIntermediateNode()
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new CSharpExpressionIntermediateNode()
|
||||
}
|
||||
};
|
||||
var extension = new TemplateTargetExtension()
|
||||
{
|
||||
TemplateTypeName = "global::TestTemplate"
|
||||
};
|
||||
|
||||
var nodeWriter = new RuntimeNodeWriter()
|
||||
{
|
||||
PushWriterMethod = "TestPushWriter",
|
||||
PopWriterMethod = "TestPopWriter"
|
||||
};
|
||||
|
||||
var context = TestCodeRenderingContext.CreateRuntime(nodeWriter: nodeWriter);
|
||||
|
||||
// Act
|
||||
extension.WriteTemplate(context, node);
|
||||
|
||||
// Assert
|
||||
var expected = @"item => new global::TestTemplate(async(__razor_template_writer) => {
|
||||
TestPushWriter(__razor_template_writer);
|
||||
Render Children
|
||||
TestPopWriter();
|
||||
}
|
||||
)";
|
||||
|
||||
var output = context.CodeWriter.GenerateCode();
|
||||
Assert.Equal(expected, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class HtmlConventionsTest
|
||||
{
|
||||
public static TheoryData HtmlConversionData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<string, string>
|
||||
{
|
||||
{ "SomeThing", "some-thing" },
|
||||
{ "someOtherThing", "some-other-thing" },
|
||||
{ "capsONInside", "caps-on-inside" },
|
||||
{ "CAPSOnOUTSIDE", "caps-on-outside" },
|
||||
{ "ALLCAPS", "allcaps" },
|
||||
{ "One1Two2Three3", "one1-two2-three3" },
|
||||
{ "ONE1TWO2THREE3", "one1two2three3" },
|
||||
{ "First_Second_ThirdHi", "first_second_third-hi" }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(HtmlConversionData))]
|
||||
public void ToHtmlCase_ReturnsExpectedConversions(string input, string expectedOutput)
|
||||
{
|
||||
// Arrange, Act
|
||||
var output = HtmlConventions.ToHtmlCase(input);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(output, expectedOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.AspNetCore.Razor.Language.Syntax;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public class HtmlNodeOptimizationPassTest
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_RewritesWhitespace()
|
||||
{
|
||||
// Assert
|
||||
var content = Environment.NewLine + " @true";
|
||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||
var originalTree = RazorSyntaxTree.Parse(sourceDocument);
|
||||
var pass = new HtmlNodeOptimizationPass();
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
// Act
|
||||
var outputTree = pass.Execute(codeDocument, originalTree);
|
||||
|
||||
// Assert
|
||||
var document = Assert.IsType<RazorDocumentSyntax>(outputTree.Root);
|
||||
var block = Assert.IsType<MarkupBlockSyntax>(document.Document);
|
||||
Assert.Equal(4, block.Children.Count);
|
||||
var whitespace = Assert.IsType<MarkupTextLiteralSyntax>(block.Children[1]);
|
||||
Assert.True(whitespace.GetContent().All(char.IsWhiteSpace));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class BasicIntegrationTest : IntegrationTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void Empty()
|
||||
{
|
||||
// Arrange
|
||||
var projectEngine = CreateProjectEngine();
|
||||
var projectItem = CreateProjectItemFromFile();
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HelloWorld()
|
||||
{
|
||||
// Arrange
|
||||
var projectEngine = CreateProjectEngine();
|
||||
var projectItem = CreateProjectItemFromFile();
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CustomDirective()
|
||||
{
|
||||
// Arrange
|
||||
var projectEngine = CreateProjectEngine(b =>
|
||||
{
|
||||
b.AddDirective(DirectiveDescriptor.CreateDirective("test", DirectiveKind.SingleLine));
|
||||
});
|
||||
|
||||
var projectItem = CreateProjectItemFromFile();
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode());
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,96 @@
|
|||
// 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 Microsoft.AspNetCore.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class ComponentBindIntegrationTest : RazorIntegrationTestBase
|
||||
{
|
||||
internal override string FileKind => FileKinds.Component;
|
||||
|
||||
internal override bool UseTwoPhaseCompilation => true;
|
||||
|
||||
[Fact]
|
||||
public void BindDuplicates_ReportsDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(Parse(@"
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
[BindElement(""div"", ""value"", ""myvalue2"", ""myevent2"")]
|
||||
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
|
||||
public static class BindAttributes
|
||||
{
|
||||
}
|
||||
}"));
|
||||
|
||||
// Act
|
||||
var result = CompileToCSharp(@"
|
||||
<div bind-value=""@ParentValue"" />
|
||||
@functions {
|
||||
public string ParentValue { get; set; } = ""hi"";
|
||||
}");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(result.Diagnostics);
|
||||
Assert.Equal("RZ9989", diagnostic.Id);
|
||||
Assert.Equal(
|
||||
"The attribute 'bind-value' was matched by multiple bind attributes. Duplicates:" + Environment.NewLine +
|
||||
"Test.BindAttributes" + Environment.NewLine +
|
||||
"Test.BindAttributes",
|
||||
diagnostic.GetMessage());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BindFallback_InvalidSyntax_TooManyParts()
|
||||
{
|
||||
// Arrange & Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<input type=""text"" bind-first-second-third=""Text"" />
|
||||
@functions {
|
||||
public string Text { get; set; } = ""text"";
|
||||
}");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Equal("RZ9991", diagnostic.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BindFallback_InvalidSyntax_TrailingDash()
|
||||
{
|
||||
// Arrange & Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<input type=""text"" bind-first-=""Text"" />
|
||||
@functions {
|
||||
public string Text { get; set; } = ""text"";
|
||||
}");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Equal("RZ9991", diagnostic.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Bind_InvalidUseOfDirective_DoesNotThrow()
|
||||
{
|
||||
var generated = CompileToCSharp(@"
|
||||
<input type=""text"" bind=""@page"" />
|
||||
@functions {
|
||||
public string page { get; set; } = ""text"";
|
||||
}");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
generated.Diagnostics,
|
||||
d => Assert.Equal("RZ2005", d.Id),
|
||||
d => Assert.Equal("RZ1011", d.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
// 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 Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Razor.Language.Components;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class ComponentChildContentIntegrationTest : RazorIntegrationTestBase
|
||||
{
|
||||
private readonly CSharpSyntaxTree RenderChildContentComponent = Parse(@"
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.RenderTree;
|
||||
namespace Test
|
||||
{
|
||||
public class RenderChildContent : ComponentBase
|
||||
{
|
||||
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
builder.AddContent(0, ChildContent);
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
RenderFragment ChildContent { get; set; }
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
private readonly CSharpSyntaxTree RenderChildContentStringComponent = Parse(@"
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.RenderTree;
|
||||
namespace Test
|
||||
{
|
||||
public class RenderChildContentString : ComponentBase
|
||||
{
|
||||
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
builder.AddContent(0, ChildContent, Value);
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
RenderFragment<string> ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
string Value { get; set; }
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
private readonly CSharpSyntaxTree RenderMultipleChildContent = Parse(@"
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.RenderTree;
|
||||
namespace Test
|
||||
{
|
||||
public class RenderMultipleChildContent : ComponentBase
|
||||
{
|
||||
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
builder.AddContent(0, Header, Name);
|
||||
builder.AddContent(1, ChildContent, Value);
|
||||
builder.AddContent(2, Footer);
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
string Name { get; set; }
|
||||
|
||||
[Parameter]
|
||||
RenderFragment<string> Header { get; set; }
|
||||
|
||||
[Parameter]
|
||||
RenderFragment<string> ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
RenderFragment Footer { get; set; }
|
||||
|
||||
[Parameter]
|
||||
string Value { get; set; }
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
internal override string FileKind => FileKinds.Component;
|
||||
|
||||
internal override bool UseTwoPhaseCompilation => true;
|
||||
|
||||
[Fact]
|
||||
public void ChildContent_AttributeAndBody_ProducesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(RenderChildContentComponent);
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
@{ RenderFragment<string> template = @<div>@context.ToLowerInvariant()</div>; }
|
||||
<RenderChildContent ChildContent=""@template.WithValue(""HI"")"">
|
||||
Some Content
|
||||
</RenderChildContent>");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Same(ComponentDiagnosticFactory.ChildContentSetByAttributeAndBody.Id, diagnostic.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChildContent_AttributeAndExplicitChildContent_ProducesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(RenderChildContentComponent);
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
@{ RenderFragment<string> template = @<div>@context.ToLowerInvariant()</div>; }
|
||||
<RenderChildContent ChildContent=""@template.WithValue(""HI"")"">
|
||||
<ChildContent>
|
||||
Some Content
|
||||
</ChildContent>
|
||||
</RenderChildContent>");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Same(ComponentDiagnosticFactory.ChildContentSetByAttributeAndBody.Id, diagnostic.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChildContent_ExplicitChildContent_UnrecogizedContent_ProducesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(RenderChildContentComponent);
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<RenderChildContent>
|
||||
<ChildContent>
|
||||
</ChildContent>
|
||||
@somethingElse
|
||||
</RenderChildContent>");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Same(ComponentDiagnosticFactory.ChildContentMixedWithExplicitChildContent.Id, diagnostic.Id);
|
||||
Assert.Equal(
|
||||
"Unrecognized child content inside component 'RenderChildContent'. The component 'RenderChildContent' accepts " +
|
||||
"child content through the following top-level items: 'ChildContent'.",
|
||||
diagnostic.GetMessage());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChildContent_ExplicitChildContent_UnrecogizedElement_ProducesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(RenderChildContentComponent);
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<RenderChildContent>
|
||||
<ChildContent>
|
||||
</ChildContent>
|
||||
<UnrecognizedChildContent></UnrecognizedChildContent>
|
||||
</RenderChildContent>");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Same(ComponentDiagnosticFactory.ChildContentMixedWithExplicitChildContent.Id, diagnostic.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChildContent_ExplicitChildContent_UnrecogizedAttribute_ProducesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(RenderChildContentComponent);
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<RenderChildContent>
|
||||
<ChildContent attr>
|
||||
</ChildContent>
|
||||
</RenderChildContent>");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Same(ComponentDiagnosticFactory.ChildContentHasInvalidAttribute.Id, diagnostic.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChildContent_ExplicitChildContent_InvalidParameterName_ProducesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(RenderChildContentStringComponent);
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<RenderChildContentString>
|
||||
<ChildContent Context=""@(""HI"")"">
|
||||
</ChildContent>
|
||||
</RenderChildContentString>");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Same(ComponentDiagnosticFactory.ChildContentHasInvalidParameter.Id, diagnostic.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChildContent_ExplicitChildContent_RepeatedParameterName_GeneratesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(RenderChildContentStringComponent);
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<RenderChildContentString>
|
||||
<ChildContent>
|
||||
<RenderChildContentString>
|
||||
<ChildContent Context=""context"">
|
||||
</ChildContent>
|
||||
</RenderChildContentString>
|
||||
</ChildContent>
|
||||
</RenderChildContentString>");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Same(ComponentDiagnosticFactory.ChildContentRepeatedParameterName.Id, diagnostic.Id);
|
||||
Assert.Equal(
|
||||
"The child content element 'ChildContent' of component 'RenderChildContentString' uses the same parameter name ('context') as enclosing child content " +
|
||||
"element 'ChildContent' of component 'RenderChildContentString'. Specify the parameter name like: '<ChildContent Context=\"another_name\"> to resolve the ambiguity",
|
||||
diagnostic.GetMessage());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChildContent_ContextParameterNameOnComponent_Invalid_ProducesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(RenderChildContentStringComponent);
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<RenderChildContentString Context=""@Foo()"">
|
||||
</RenderChildContentString>");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Same(ComponentDiagnosticFactory.ChildContentHasInvalidParameterOnComponent.Id, diagnostic.Id);
|
||||
Assert.Equal(
|
||||
"Invalid parameter name. The parameter name attribute 'Context' on component 'RenderChildContentString' can only include literal text.",
|
||||
diagnostic.GetMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,175 @@
|
|||
// 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.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.RenderTree;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class ComponentDeclarationRazorIntegrationTest : RazorIntegrationTestBase
|
||||
{
|
||||
public ComponentDeclarationRazorIntegrationTest()
|
||||
{
|
||||
// Include this assembly to use types defined in tests.
|
||||
BaseCompilation = DefaultBaseCompilation.AddReferences(MetadataReference.CreateFromFile(GetType().Assembly.Location));
|
||||
}
|
||||
|
||||
internal override CSharpCompilation BaseCompilation { get; }
|
||||
|
||||
internal override string FileKind => FileKinds.Component;
|
||||
|
||||
internal override bool DeclarationOnly => true;
|
||||
|
||||
[Fact]
|
||||
public void DeclarationConfiguration_IncludesFunctions()
|
||||
{
|
||||
// Arrange & Act
|
||||
var component = CompileToComponent(@"
|
||||
@functions {
|
||||
public string Value { get; set; }
|
||||
}");
|
||||
|
||||
// Assert
|
||||
var property = component.GetType().GetProperty("Value");
|
||||
Assert.NotNull(property);
|
||||
Assert.Same(typeof(string), property.PropertyType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeclarationConfiguration_IncludesInject()
|
||||
{
|
||||
// Arrange & Act
|
||||
var component = CompileToComponent(@"
|
||||
@inject string Value
|
||||
");
|
||||
|
||||
// Assert
|
||||
var property = component.GetType().GetProperty("Value", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
Assert.NotNull(property);
|
||||
Assert.Same(typeof(string), property.PropertyType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeclarationConfiguration_IncludesUsings()
|
||||
{
|
||||
// Arrange & Act
|
||||
var component = CompileToComponent(@"
|
||||
@using System.Text
|
||||
@inject StringBuilder Value
|
||||
");
|
||||
|
||||
// Assert
|
||||
var property = component.GetType().GetProperty("Value", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
Assert.NotNull(property);
|
||||
Assert.Same(typeof(StringBuilder), property.PropertyType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeclarationConfiguration_IncludesInherits()
|
||||
{
|
||||
// Arrange & Act
|
||||
var component = CompileToComponent($@"
|
||||
@inherits {FullTypeName<BaseClass>()}
|
||||
");
|
||||
|
||||
// Assert
|
||||
Assert.Same(typeof(BaseClass), component.GetType().BaseType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeclarationConfiguration_IncludesImplements()
|
||||
{
|
||||
// Arrange & Act
|
||||
var component = CompileToComponent($@"
|
||||
@implements {FullTypeName<IDoCoolThings>()}
|
||||
");
|
||||
|
||||
// Assert
|
||||
var type = component.GetType();
|
||||
Assert.Contains(typeof(IDoCoolThings), component.GetType().GetInterfaces());
|
||||
}
|
||||
|
||||
[Fact] // Regression test for https://github.com/aspnet/Blazor/issues/453
|
||||
public void DeclarationConfiguration_FunctionsBlockHasLineMappings_MappingsApplyToError()
|
||||
{
|
||||
// Arrange & Act 1
|
||||
var generated = CompileToCSharp(@"
|
||||
@functions {
|
||||
public StringBuilder Builder { get; set; }
|
||||
}
|
||||
");
|
||||
|
||||
// Assert 1
|
||||
AssertSourceEquals(@"
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
#pragma warning disable 0414
|
||||
#pragma warning disable 0649
|
||||
#pragma warning disable 0169
|
||||
|
||||
namespace Test
|
||||
{
|
||||
#line hidden
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
|
||||
{
|
||||
#pragma warning disable 1998
|
||||
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
|
||||
{
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
#nullable restore
|
||||
#line 1 ""x:\dir\subdir\Test\TestComponent.cshtml""
|
||||
|
||||
public StringBuilder Builder { get; set; }
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
", generated);
|
||||
|
||||
// Act 2
|
||||
var assembly = CompileToAssembly(generated, throwOnFailure: false);
|
||||
|
||||
// Assert 2
|
||||
var diagnostic = Assert.Single(assembly.Diagnostics);
|
||||
|
||||
// This error should map to line 2 of the generated file, the test
|
||||
// says 1 because Roslyn's line/column data structures are 0-based.
|
||||
var position = diagnostic.Location.GetMappedLineSpan();
|
||||
Assert.EndsWith(".cshtml", position.Path);
|
||||
Assert.Equal(1, position.StartLinePosition.Line);
|
||||
}
|
||||
|
||||
public class BaseClass : IComponent
|
||||
{
|
||||
public void Init(RenderHandle renderHandle)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetParameters(ParameterCollection parameters)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDoCoolThings
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class ComponentDesignTimeCodeGenerationTest : ComponentCodeGenerationTestBase
|
||||
{
|
||||
internal override bool DesignTime => true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class ComponentDiagnosticRazorIntegrationTest : RazorIntegrationTestBase
|
||||
{
|
||||
internal override string FileKind => FileKinds.Component;
|
||||
|
||||
[Fact]
|
||||
public void RejectsEndTagWithNoStartTag()
|
||||
{
|
||||
// Arrange/Act
|
||||
var result = CompileToCSharp(
|
||||
"Line1\nLine2\nLine3</mytag>");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Diagnostics,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ9981", item.Id);
|
||||
Assert.Equal("Unexpected closing tag 'mytag' with no matching start tag.", item.GetMessage());
|
||||
});
|
||||
}
|
||||
|
||||
// This used to be a sugar syntax for lambdas, but we don't support that anymore
|
||||
[Fact]
|
||||
public void OldCodeBlockAttributeSyntax_ReportsError()
|
||||
{
|
||||
// Arrange/Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<elem attr=@{ DidInvokeCode = true; } />
|
||||
@functions {
|
||||
public bool DidInvokeCode { get; set; } = false;
|
||||
}");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Equal("RZ9979", diagnostic.Id);
|
||||
Assert.NotNull(diagnostic.GetMessage());
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RejectsScriptTag()
|
||||
{
|
||||
// Arrange/Act
|
||||
var result = CompileToCSharp(@"Hello
|
||||
<div>
|
||||
<script src='anything'>
|
||||
something
|
||||
</script>
|
||||
</div>
|
||||
Goodbye");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Diagnostics,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ9992", item.Id);
|
||||
Assert.Equal("Script tags should not be placed inside components because they cannot be updated dynamically. To fix this, move the script tag to the 'index.html' file or another static location. For more information see https://go.microsoft.com/fwlink/?linkid=872131", item.GetMessage());
|
||||
Assert.Equal(2, item.Span.LineIndex);
|
||||
Assert.Equal(4, item.Span.CharacterIndex);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RejectsTagHelperDirectives()
|
||||
{
|
||||
// Arrange/Act
|
||||
AdditionalSyntaxTrees.Add(Parse(@"
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
public class MyComponent : ComponentBase
|
||||
{
|
||||
}
|
||||
}
|
||||
"));
|
||||
|
||||
var result = CompileToCSharp(@"
|
||||
@addTagHelper *, TestAssembly
|
||||
@tagHelperPrefix th
|
||||
|
||||
<MyComponent />
|
||||
");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Diagnostics,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ9978", item.Id);
|
||||
Assert.Equal("The directives @addTagHelper, @removeTagHelper and @tagHelperPrefix are not valid in a component document. " +
|
||||
"Use '@using <namespace>' directive instead.", item.GetMessage());
|
||||
Assert.Equal(0, item.Span.LineIndex);
|
||||
Assert.Equal(0, item.Span.CharacterIndex);
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ9978", item.Id);
|
||||
Assert.Equal("The directives @addTagHelper, @removeTagHelper and @tagHelperPrefix are not valid in a component document. " +
|
||||
"Use '@using <namespace>' directive instead.", item.GetMessage());
|
||||
Assert.Equal(1, item.Span.LineIndex);
|
||||
Assert.Equal(0, item.Span.CharacterIndex);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
// 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.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Layouts;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
// Integration tests for component directives
|
||||
public class ComponentDirectiveIntegrationTest : RazorIntegrationTestBase
|
||||
{
|
||||
public ComponentDirectiveIntegrationTest()
|
||||
{
|
||||
// Include this assembly to use types defined in tests.
|
||||
BaseCompilation = DefaultBaseCompilation.AddReferences(MetadataReference.CreateFromFile(GetType().Assembly.Location));
|
||||
}
|
||||
|
||||
internal override CSharpCompilation BaseCompilation { get; }
|
||||
|
||||
internal override string FileKind => FileKinds.Component;
|
||||
|
||||
[Fact]
|
||||
public void ComponentsDoNotHaveLayoutAttributeByDefault()
|
||||
{
|
||||
// Arrange/Act
|
||||
var component = CompileToComponent($"Hello");
|
||||
|
||||
// Assert
|
||||
Assert.Null(component.GetType().GetCustomAttribute<LayoutAttribute>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsLayoutDeclarations()
|
||||
{
|
||||
// Arrange/Act
|
||||
var testComponentTypeName = FullTypeName<TestLayout>();
|
||||
var component = CompileToComponent(
|
||||
$"@layout {testComponentTypeName}\n" +
|
||||
$"Hello");
|
||||
|
||||
// Assert
|
||||
var layoutAttribute = component.GetType().GetCustomAttribute<LayoutAttribute>();
|
||||
Assert.NotNull(layoutAttribute);
|
||||
Assert.Equal(typeof(TestLayout), layoutAttribute.LayoutType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsImplementsDeclarations()
|
||||
{
|
||||
// Arrange/Act
|
||||
var testInterfaceTypeName = FullTypeName<ITestInterface>();
|
||||
var component = CompileToComponent(
|
||||
$"@implements {testInterfaceTypeName}\n" +
|
||||
$"Hello");
|
||||
|
||||
// Assert
|
||||
Assert.IsAssignableFrom<ITestInterface>(component);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsMultipleImplementsDeclarations()
|
||||
{
|
||||
// Arrange/Act
|
||||
var testInterfaceTypeName = FullTypeName<ITestInterface>();
|
||||
var testInterfaceTypeName2 = FullTypeName<ITestInterface2>();
|
||||
var component = CompileToComponent(
|
||||
$"@implements {testInterfaceTypeName}\n" +
|
||||
$"@implements {testInterfaceTypeName2}\n" +
|
||||
$"Hello");
|
||||
|
||||
// Assert
|
||||
Assert.IsAssignableFrom<ITestInterface>(component);
|
||||
Assert.IsAssignableFrom<ITestInterface2>(component);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsInheritsDirective()
|
||||
{
|
||||
// Arrange/Act
|
||||
var testBaseClassTypeName = FullTypeName<TestBaseClass>();
|
||||
var component = CompileToComponent(
|
||||
$"@inherits {testBaseClassTypeName}" + Environment.NewLine +
|
||||
$"Hello");
|
||||
|
||||
// Assert
|
||||
Assert.IsAssignableFrom<TestBaseClass>(component);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsInjectDirective()
|
||||
{
|
||||
// Arrange/Act 1: Compilation
|
||||
var componentType = CompileToComponent(
|
||||
$"@inject {FullTypeName<IMyService1>()} MyService1\n" +
|
||||
$"@inject {FullTypeName<IMyService2>()} MyService2\n" +
|
||||
$"Hello from @MyService1 and @MyService2").GetType();
|
||||
|
||||
// Assert 1: Compiled type has correct properties
|
||||
var propertyFlags = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
var injectableProperties = componentType.GetProperties(propertyFlags)
|
||||
.Where(p => p.GetCustomAttribute<InjectAttribute>() != null);
|
||||
Assert.Collection(injectableProperties.OrderBy(p => p.Name),
|
||||
property =>
|
||||
{
|
||||
Assert.Equal("MyService1", property.Name);
|
||||
Assert.Equal(typeof(IMyService1), property.PropertyType);
|
||||
Assert.False(property.GetMethod.IsPublic);
|
||||
Assert.False(property.SetMethod.IsPublic);
|
||||
},
|
||||
property =>
|
||||
{
|
||||
Assert.Equal("MyService2", property.Name);
|
||||
Assert.Equal(typeof(IMyService2), property.PropertyType);
|
||||
Assert.False(property.GetMethod.IsPublic);
|
||||
Assert.False(property.SetMethod.IsPublic);
|
||||
});
|
||||
}
|
||||
|
||||
public class TestLayout : IComponent
|
||||
{
|
||||
[Parameter]
|
||||
RenderFragment Body { get; set; }
|
||||
|
||||
public void Init(RenderHandle renderHandle)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetParameters(ParameterCollection parameters)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public interface ITestInterface { }
|
||||
|
||||
public interface ITestInterface2 { }
|
||||
|
||||
public class TestBaseClass : ComponentBase { }
|
||||
|
||||
public interface IMyService1 { }
|
||||
public interface IMyService2 { }
|
||||
public class MyService1Impl : IMyService1 { }
|
||||
public class MyService2Impl : IMyService2 { }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class ComponentDiscoveryIntegrationTest : RazorIntegrationTestBase
|
||||
{
|
||||
internal override string FileKind => FileKinds.Component;
|
||||
|
||||
internal override bool UseTwoPhaseCompilation => true;
|
||||
|
||||
[Fact]
|
||||
public void ComponentDiscovery_CanFindComponent_DefinedinCSharp()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(Parse(@"
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
public class MyComponent : ComponentBase
|
||||
{
|
||||
}
|
||||
}
|
||||
"));
|
||||
|
||||
// Act
|
||||
var result = CompileToCSharp(string.Empty);
|
||||
|
||||
// Assert
|
||||
var bindings = result.CodeDocument.GetTagHelperContext();
|
||||
Assert.Contains(bindings.TagHelpers, t => t.Name == "Test.MyComponent");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComponentDiscovery_CanFindComponent_WithNamespace_DefinedinCSharp()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(Parse(@"
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Test.AnotherNamespace
|
||||
{
|
||||
public class MyComponent : ComponentBase
|
||||
{
|
||||
}
|
||||
}
|
||||
"));
|
||||
|
||||
// Act
|
||||
var result = CompileToCSharp(string.Empty);
|
||||
|
||||
// Assert
|
||||
var bindings = result.CodeDocument.GetTagHelperContext();
|
||||
|
||||
Assert.Contains(bindings.TagHelpers, t =>
|
||||
{
|
||||
return t.Name == "Test.AnotherNamespace.MyComponent" &&
|
||||
t.IsComponentFullyQualifiedNameMatch();
|
||||
});
|
||||
|
||||
Assert.DoesNotContain(bindings.TagHelpers, t =>
|
||||
{
|
||||
return t.Name == "Test.AnotherNamespace.MyComponent" &&
|
||||
!t.IsComponentFullyQualifiedNameMatch();
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComponentDiscovery_CanFindComponent_DefinedinCshtml()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var result = CompileToCSharp("UniqueName.cshtml", string.Empty);
|
||||
|
||||
// Assert
|
||||
var bindings = result.CodeDocument.GetTagHelperContext();
|
||||
Assert.Contains(bindings.TagHelpers, t => t.Name == "Test.UniqueName");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComponentDiscovery_CanFindComponent_WithTypeParameter()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var result = CompileToCSharp("UniqueName.cshtml", @"
|
||||
@typeparam TItem
|
||||
@functions {
|
||||
[Parameter] TItem Item { get; set; }
|
||||
}");
|
||||
|
||||
// Assert
|
||||
var bindings = result.CodeDocument.GetTagHelperContext();
|
||||
Assert.Contains(bindings.TagHelpers, t => t.Name == "Test.UniqueName<TItem>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComponentDiscovery_CanFindComponent_WithMultipleTypeParameters()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var result = CompileToCSharp("UniqueName.cshtml", @"
|
||||
@typeparam TItem1
|
||||
@typeparam TItem2
|
||||
@typeparam TItem3
|
||||
@functions {
|
||||
[Parameter] TItem1 Item { get; set; }
|
||||
}");
|
||||
|
||||
// Assert
|
||||
var bindings = result.CodeDocument.GetTagHelperContext();
|
||||
Assert.Contains(bindings.TagHelpers, t => t.Name == "Test.UniqueName<TItem1, TItem2, TItem3>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// 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.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
// Integration tests focused on file path handling for class/namespace names
|
||||
public class ComponentFilePathIntegrationTest : RazorIntegrationTestBase
|
||||
{
|
||||
internal override string FileKind => FileKinds.Component;
|
||||
|
||||
[Fact]
|
||||
public void FileNameIsInvalidClassName_SanitizesInvalidClassName()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var result = CompileToAssembly("Filename with spaces.cshtml", "");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.Diagnostics);
|
||||
|
||||
var type = Assert.Single(result.Assembly.GetTypes());
|
||||
Assert.Equal(DefaultRootNamespace, type.Namespace);
|
||||
Assert.Equal("Filename_with_spaces", type.Name);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ItemAtRoot.cs", "Test", "ItemAtRoot")]
|
||||
[InlineData("Dir1\\MyFile.cs", "Test.Dir1", "MyFile")]
|
||||
[InlineData("Dir1\\Dir2\\MyFile.cs", "Test.Dir1.Dir2", "MyFile")]
|
||||
public void CreatesClassWithCorrectNameAndNamespace(string relativePath, string expectedNamespace, string expectedClassName)
|
||||
{
|
||||
// Arrange
|
||||
relativePath = relativePath.Replace('\\', Path.DirectorySeparatorChar);
|
||||
|
||||
// Act
|
||||
var result = CompileToAssembly(relativePath, "");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.Diagnostics);
|
||||
|
||||
var type = Assert.Single(result.Assembly.GetTypes());
|
||||
Assert.Equal(expectedNamespace, type.Namespace);
|
||||
Assert.Equal(expectedClassName, type.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// 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 Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Razor.Language.Components;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class ComponentGenericTypeIntegrationTest : RazorIntegrationTestBase
|
||||
{
|
||||
private readonly CSharpSyntaxTree GenericContextComponent = Parse(@"
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.RenderTree;
|
||||
namespace Test
|
||||
{
|
||||
public class GenericContext<TItem> : ComponentBase
|
||||
{
|
||||
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
var items = (IReadOnlyList<TItem>)Items ?? Array.Empty<TItem>();
|
||||
for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (ChildContent == null)
|
||||
{
|
||||
builder.AddContent(i, Items[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AddContent(i, ChildContent, new Context() { Index = i, Item = items[i], });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
List<TItem> Items { get; set; }
|
||||
|
||||
[Parameter]
|
||||
RenderFragment<Context> ChildContent { get; set; }
|
||||
|
||||
public class Context
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public TItem Item { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
private readonly CSharpSyntaxTree MultipleGenericParameterComponent = Parse(@"
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.RenderTree;
|
||||
namespace Test
|
||||
{
|
||||
public class MultipleGenericParameter<TItem1, TItem2, TItem3> : ComponentBase
|
||||
{
|
||||
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
builder.AddContent(0, Item1);
|
||||
builder.AddContent(1, Item2);
|
||||
builder.AddContent(2, Item3);
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
TItem1 Item1 { get; set; }
|
||||
|
||||
[Parameter]
|
||||
TItem2 Item2 { get; set; }
|
||||
|
||||
[Parameter]
|
||||
TItem3 Item3 { get; set; }
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
internal override string FileKind => FileKinds.Component;
|
||||
|
||||
internal override bool UseTwoPhaseCompilation => true;
|
||||
|
||||
[Fact]
|
||||
public void GenericComponent_WithoutAnyTypeParameters_TriggersDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(GenericContextComponent);
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<GenericContext />");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Same(ComponentDiagnosticFactory.GenericComponentTypeInferenceUnderspecified.Id, diagnostic.Id);
|
||||
Assert.Equal(
|
||||
"The type of component 'GenericContext' cannot be inferred based on the values provided. Consider " +
|
||||
"specifying the type arguments directly using the following attributes: 'TItem'.",
|
||||
diagnostic.GetMessage());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenericComponent_WithMissingTypeParameters_TriggersDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(MultipleGenericParameterComponent);
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<MultipleGenericParameter TItem1=int />");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Same(ComponentDiagnosticFactory.GenericComponentMissingTypeArgument.Id, diagnostic.Id);
|
||||
Assert.Equal(
|
||||
"The component 'MultipleGenericParameter' is missing required type arguments. " +
|
||||
"Specify the missing types using the attributes: 'TItem2', 'TItem3'.",
|
||||
diagnostic.GetMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class ComponentImportsIntegrationTest : RazorIntegrationTestBase
|
||||
{
|
||||
internal override string FileKind => FileKinds.ComponentImport;
|
||||
|
||||
[Fact]
|
||||
public void NoErrorsForUsingStatements()
|
||||
{
|
||||
// Arrange/Act
|
||||
var result = CompileToCSharp("_Imports.razor", @"
|
||||
@using System.Text
|
||||
@using System.Reflection
|
||||
@* This is allowed in imports *@
|
||||
");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.Diagnostics);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoErrorsForRazorComments()
|
||||
{
|
||||
// Arrange/Act
|
||||
var result = CompileToCSharp("_Imports.razor", @"
|
||||
@* This is allowed in imports *@
|
||||
");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.Diagnostics);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoErrorsForSupportedDirectives()
|
||||
{
|
||||
// Arrange/Act
|
||||
var result = CompileToCSharp("_Imports.razor", @"
|
||||
@inject FooService Foo
|
||||
@typeparam TItem
|
||||
@implements ISomeInterface
|
||||
@inherits SomeNamespace.SomeBaseType
|
||||
");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.Diagnostics);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ErrorsForPageDirective()
|
||||
{
|
||||
// Arrange/Act
|
||||
var result = CompileToCSharp("_Imports.razor", @"
|
||||
@page ""/""
|
||||
");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Diagnostics,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ9987", item.Id);
|
||||
Assert.Equal(@"The '@page' directive specified in _Imports.razor file will not be imported. The directive must appear at the top of each Razor file", item.GetMessage());
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ErrorsForTagHelperDirectives()
|
||||
{
|
||||
// Arrange/Act
|
||||
var result = CompileToCSharp("_Imports.razor", @"
|
||||
@addTagHelper *, TestAssembly
|
||||
@removeTagHelper *, TestAssembly
|
||||
@tagHelperPrefix th:
|
||||
");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Diagnostics,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ9978", item.Id);
|
||||
Assert.Equal(0, item.Span.LineIndex);
|
||||
Assert.Equal(@"The directives @addTagHelper, @removeTagHelper and @tagHelperPrefix are not valid in a component document. Use '@using <namespace>' directive instead.", item.GetMessage());
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ9978", item.Id);
|
||||
Assert.Equal(1, item.Span.LineIndex);
|
||||
Assert.Equal(@"The directives @addTagHelper, @removeTagHelper and @tagHelperPrefix are not valid in a component document. Use '@using <namespace>' directive instead.", item.GetMessage());
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ9978", item.Id);
|
||||
Assert.Equal(2, item.Span.LineIndex);
|
||||
Assert.Equal(@"The directives @addTagHelper, @removeTagHelper and @tagHelperPrefix are not valid in a component document. Use '@using <namespace>' directive instead.", item.GetMessage());
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ErrorsForFunctionsDirective()
|
||||
{
|
||||
// Arrange/Act
|
||||
var result = CompileToCSharp("_Imports.razor", @"
|
||||
@functions {
|
||||
public class Test
|
||||
{
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Diagnostics,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ10003", item.Id);
|
||||
Assert.Equal(@"Markup, code and block directives are not valid in component imports.", item.GetMessage());
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ErrorsForSectionDirective()
|
||||
{
|
||||
// Arrange/Act
|
||||
var result = CompileToCSharp("_Imports.razor", @"
|
||||
@section Foo {
|
||||
}
|
||||
");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Diagnostics,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ10003", item.Id);
|
||||
Assert.Equal(@"Markup, code and block directives are not valid in component imports.", item.GetMessage());
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ErrorsForMarkup()
|
||||
{
|
||||
// Arrange/Act
|
||||
var result = CompileToCSharp("_Imports.razor", @"
|
||||
<div>asdf</div>
|
||||
");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Diagnostics,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ10003", item.Id);
|
||||
Assert.Equal(@"Markup, code and block directives are not valid in component imports.", item.GetMessage());
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ErrorsForCode()
|
||||
{
|
||||
// Arrange/Act
|
||||
var result = CompileToCSharp("_Imports.razor", @"
|
||||
@Foo
|
||||
@(Bar)
|
||||
@{
|
||||
var x = Foo;
|
||||
}");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Diagnostics,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ10003", item.Id);
|
||||
Assert.Equal(0, item.Span.LineIndex);
|
||||
Assert.Equal(@"Markup, code and block directives are not valid in component imports.", item.GetMessage());
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ10003", item.Id);
|
||||
Assert.Equal(1, item.Span.LineIndex);
|
||||
Assert.Equal(@"Markup, code and block directives are not valid in component imports.", item.GetMessage());
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("RZ10003", item.Id);
|
||||
Assert.Equal(2, item.Span.LineIndex);
|
||||
Assert.Equal(@"Markup, code and block directives are not valid in component imports.", item.GetMessage());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class ComponentRuntimeCodeGenerationTest : ComponentCodeGenerationTestBase
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class ComponentTemplateIntegrationTest : RazorIntegrationTestBase
|
||||
{
|
||||
internal override string FileKind => FileKinds.Component;
|
||||
|
||||
// Razor doesn't parse this as a template, we don't need much special handling for
|
||||
// it because it will just be invalid in general.
|
||||
[Fact]
|
||||
public void Template_ImplicitExpressionInMarkupAttribute_CreatesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"<div attr=""@<div></div>"" />");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Equal("RZ1005", diagnostic.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Template_ExplicitExpressionInMarkupAttribute_CreatesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"<div attr=""@(@<div></div>)"" />");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Equal("RZ9994", diagnostic.Id);
|
||||
}
|
||||
|
||||
// Razor doesn't parse this as a template, we don't need much special handling for
|
||||
// it because it will just be invalid in general.
|
||||
[Fact]
|
||||
public void Template_ImplicitExpressionInComponentAttribute_CreatesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(Parse(@"
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
public class MyComponent : ComponentBase
|
||||
{
|
||||
}
|
||||
}
|
||||
"));
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"<MyComponent attr=""@<div></div>"" />");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Equal("RZ1005", diagnostic.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Template_ExplicitExpressionInComponentAttribute_CreatesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(Parse(@"
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
public class MyComponent : ComponentBase
|
||||
{
|
||||
}
|
||||
}
|
||||
"));
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"<MyComponent attr=""@(@<div></div>)"" />");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Equal("RZ9994", diagnostic.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Template_ExplicitExpressionInRef_CreatesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"<div ref=""@(@<div></div>)"" />");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Equal("RZ9994", diagnostic.Id);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Template_ExplicitExpressionInBind_CreatesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"<input type=""text"" bind=""@(@<div></div>)"" />");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Equal("RZ9994", diagnostic.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Template_ExplicitExpressionInEventHandler_CreatesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"<input type=""text"" onchange=""@(@<div></div>)"" />");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Equal("RZ9994", diagnostic.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
// 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.Linq;
|
||||
using Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
// Similar to design time code generation tests, but goes a character at a time.
|
||||
// Don't add many of these since they are slow - instead add features to existing
|
||||
// tests here, and use these as smoke tests, not for detailed regression testing.
|
||||
public class ComponentTypingTest : RazorIntegrationTestBase
|
||||
{
|
||||
internal override bool DesignTime => true;
|
||||
|
||||
internal override string FileKind => FileKinds.Component;
|
||||
|
||||
internal override bool UseTwoPhaseCompilation => true;
|
||||
|
||||
[Fact]
|
||||
public void DoSomeTyping()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(Parse(@"
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
public class MyComponent : ComponentBase
|
||||
{
|
||||
[Parameter] int Value { get; set; }
|
||||
[Parameter] Action<int> ValueChanged { get; set; }
|
||||
[Parameter] string AnotherValue { get; set; }
|
||||
}
|
||||
|
||||
public class ModelState
|
||||
{
|
||||
public Action<string> Bind(Func<string, string> func) => throw null;
|
||||
}
|
||||
}
|
||||
"));
|
||||
var text = @"
|
||||
<div>
|
||||
<MyComponent bind-Value=""myValue"" AnotherValue=""hi""/>
|
||||
<input type=""text"" bind=""@this.ModelState.Bind(x => x)"" />
|
||||
<button ref=""_button"" onsubmit=""@FormSubmitted"">Click me</button>
|
||||
</div>
|
||||
<MyComponent
|
||||
IntProperty=""123""
|
||||
BoolProperty=""true""
|
||||
StringProperty=""My string""
|
||||
ObjectProperty=""new SomeType()""/>
|
||||
@functions {
|
||||
Test.ModelState ModelState { get; set; }
|
||||
}";
|
||||
|
||||
for (var i = 0; i <= text.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
CompileToCSharp(text.Substring(0, i), throwOnFailure: false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new XunitException($@"
|
||||
Code generation failed on iteration {i} with source text:
|
||||
{text.Substring(0, i)}
|
||||
|
||||
Exception:
|
||||
{ex}
|
||||
");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact] // Regression test for #1068
|
||||
public void Regression_1068()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<input type=""text"" bind="" />
|
||||
@functions {
|
||||
Test.ModelState ModelState { get; set; }
|
||||
}
|
||||
", throwOnFailure: false);
|
||||
|
||||
// Assert
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MalformedAttributeContent()
|
||||
{
|
||||
// Act
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(Parse(@"
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
public class MyComponent : ComponentBase
|
||||
{
|
||||
[Parameter] int Value { get; set; }
|
||||
[Parameter] Action<int> ValueChanged { get; set; }
|
||||
[Parameter] string AnotherValue { get; set; }
|
||||
}
|
||||
|
||||
public class ModelState
|
||||
{
|
||||
public Action<string> Bind(Func<string, string> func) => throw null;
|
||||
}
|
||||
}
|
||||
"));
|
||||
var generated = CompileToCSharp(@"
|
||||
<MyComponent Value=10 Something=@for
|
||||
|
||||
<button disabled=@form.IsSubmitting type=""submit"" class=""btn btn-primary mt-3 mr-3 has-spinner @(form.IsSubmitting ? ""active"" :"""")"" onclick=@(async () => await SaveAsync(false))>
|
||||
@functions {
|
||||
Test.ModelState ModelState { get; set; }
|
||||
}", throwOnFailure: false);
|
||||
|
||||
// Assert - does not throw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
// Extensible directives only have codegen for design time, so we're only testing that.
|
||||
public class ExtensibleDirectiveTest : IntegrationTestBase
|
||||
{
|
||||
public ExtensibleDirectiveTest()
|
||||
: base(generateBaselines: null)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NamespaceToken()
|
||||
{
|
||||
// Arrange
|
||||
var engine = CreateProjectEngine(builder =>
|
||||
{
|
||||
builder.ConfigureDocumentClassifier();
|
||||
|
||||
builder.AddDirective(DirectiveDescriptor.CreateDirective("custom", DirectiveKind.SingleLine, b => b.AddNamespaceToken()));
|
||||
});
|
||||
|
||||
var projectItem = CreateProjectItemFromFile();
|
||||
|
||||
// Act
|
||||
var codeDocument = engine.ProcessDesignTime(projectItem);
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode());
|
||||
AssertCSharpDocumentMatchesBaseline(codeDocument.GetCSharpDocument());
|
||||
AssertSourceMappingsMatchBaseline(codeDocument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class HtmlAttributeIntegrationTest : IntegrationTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void HtmlWithDataDashAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = CreateProjectItemFromFile();
|
||||
|
||||
// Act
|
||||
var compiled = CompileToCSharp(projectItem);
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlWithConditionalAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = CreateProjectItemFromFile();
|
||||
|
||||
// Act
|
||||
var compiled = CompileToCSharp(projectItem);
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class TagHelpersIntegrationTest : IntegrationTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void SimpleTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var descriptors = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly")
|
||||
};
|
||||
|
||||
var projectEngine = CreateProjectEngine(builder => builder.AddTagHelpers(descriptors));
|
||||
var projectItem = CreateProjectItemFromFile();
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TagHelpersWithBoundAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var descriptors = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("bound")
|
||||
.PropertyName("FooProp")
|
||||
.TypeName("System.String"),
|
||||
})
|
||||
};
|
||||
|
||||
var projectEngine = CreateProjectEngine(builder => builder.AddTagHelpers(descriptors));
|
||||
var projectItem = CreateProjectItemFromFile();
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var descriptors = new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "p",
|
||||
typeName: "PTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "form",
|
||||
typeName: "FormTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("value")
|
||||
.PropertyName("FooProp")
|
||||
.TypeName("System.String"),
|
||||
})
|
||||
};
|
||||
|
||||
var projectEngine = CreateProjectEngine(builder => builder.AddTagHelpers(descriptors));
|
||||
var projectItem = CreateProjectItemFromFile();
|
||||
|
||||
// Act
|
||||
var codeDocument = projectEngine.Process(projectItem);
|
||||
|
||||
// Assert
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
var irTree = codeDocument.GetDocumentIntermediateNode();
|
||||
AssertDocumentNodeMatchesBaseline(codeDocument.GetDocumentIntermediateNode());
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor CreateTagHelperDescriptor(
|
||||
string tagName,
|
||||
string typeName,
|
||||
string assemblyName,
|
||||
IEnumerable<Action<BoundAttributeDescriptorBuilder>> attributes = null)
|
||||
{
|
||||
var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||
builder.TypeName(typeName);
|
||||
|
||||
if (attributes != null)
|
||||
{
|
||||
foreach (var attributeBuilder in attributes)
|
||||
{
|
||||
builder.BoundAttributeDescriptor(attributeBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
builder.TagMatchingRuleDescriptor(ruleBuilder => ruleBuilder.RequireTagName(tagName));
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,637 @@
|
|||
// 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.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
||||
{
|
||||
public class TestTagHelperDescriptors
|
||||
{
|
||||
public static IEnumerable<TagHelperDescriptor> SimpleTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "span",
|
||||
typeName: "SpanTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "div",
|
||||
typeName: "DivTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("value")
|
||||
.PropertyName("FooProp")
|
||||
.TypeName("System.String"),
|
||||
builder => builder
|
||||
.Name("bound")
|
||||
.PropertyName("BoundProp")
|
||||
.TypeName("System.String"),
|
||||
builder => builder
|
||||
.Name("age")
|
||||
.PropertyName("AgeProp")
|
||||
.TypeName("System.Int32"),
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TagHelperDescriptor> MinimizedBooleanTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "span",
|
||||
typeName: "SpanTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "div",
|
||||
typeName: "DivTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("value")
|
||||
.PropertyName("FooProp")
|
||||
.TypeName("System.String"),
|
||||
builder => builder
|
||||
.Name("bound")
|
||||
.PropertyName("BoundProp")
|
||||
.TypeName("System.Boolean"),
|
||||
builder => builder
|
||||
.Name("age")
|
||||
.PropertyName("AgeProp")
|
||||
.TypeName("System.Int32"),
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TagHelperDescriptor> CssSelectorTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
var inputTypePropertyInfo = typeof(TestType).GetRuntimeProperty("Type");
|
||||
var inputCheckedPropertyInfo = typeof(TestType).GetRuntimeProperty("Checked");
|
||||
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "a",
|
||||
typeName: "TestNamespace.ATagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.RequireAttributeDescriptor(attribute => attribute
|
||||
.Name("href")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("~/")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "a",
|
||||
typeName: "TestNamespace.ATagHelperMultipleSelectors",
|
||||
assemblyName: "TestAssembly",
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.RequireAttributeDescriptor(attribute => attribute
|
||||
.Name("href")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("~/")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch))
|
||||
.RequireAttributeDescriptor(attribute => attribute
|
||||
.Name("href")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("?hello=world")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch)),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.RequireAttributeDescriptor(attribute => attribute
|
||||
.Name("type")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("text")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper2",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.RequireAttributeDescriptor(attribute => attribute
|
||||
.Name("ty")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.RequireAttributeDescriptor(attribute => attribute
|
||||
.Name("href")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("~/")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch)),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper2",
|
||||
assemblyName: "TestAssembly",
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.RequireAttributeDescriptor(attribute => attribute
|
||||
.Name("type")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TagHelperDescriptor> EnumTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("catch-all")
|
||||
.PropertyName("CatchAll")
|
||||
.AsEnum()
|
||||
.TypeName($"{typeof(TestTagHelperDescriptors).FullName}.{nameof(MyEnum)}"),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("value")
|
||||
.PropertyName("Value")
|
||||
.AsEnum()
|
||||
.TypeName($"{typeof(TestTagHelperDescriptors).FullName}.{nameof(MyEnum)}"),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TagHelperDescriptor> SymbolBoundTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("[item]")
|
||||
.PropertyName("ListItems")
|
||||
.TypeName("System.Collections.Generic.List<string>"),
|
||||
builder => builder
|
||||
.Name("[(item)]")
|
||||
.PropertyName("ArrayItems")
|
||||
.TypeName(typeof(string[]).FullName),
|
||||
builder => builder
|
||||
.Name("(click)")
|
||||
.PropertyName("Event1")
|
||||
.TypeName(typeof(Action).FullName),
|
||||
builder => builder
|
||||
.Name("(^click)")
|
||||
.PropertyName("Event2")
|
||||
.TypeName(typeof(Action).FullName),
|
||||
builder => builder
|
||||
.Name("*something")
|
||||
.PropertyName("StringProperty1")
|
||||
.TypeName(typeof(string).FullName),
|
||||
builder => builder
|
||||
.Name("#local")
|
||||
.PropertyName("StringProperty2")
|
||||
.TypeName(typeof(string).FullName),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("bound")),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TagHelperDescriptor> MinimizedTagHelpers_Descriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("catchall-bound-string")
|
||||
.PropertyName("BoundRequiredString")
|
||||
.TypeName(typeof(string).FullName),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("catchall-unbound-required")),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("input-bound-required-string")
|
||||
.PropertyName("BoundRequiredString")
|
||||
.TypeName(typeof(string).FullName),
|
||||
builder => builder
|
||||
.Name("input-bound-string")
|
||||
.PropertyName("BoundString")
|
||||
.TypeName(typeof(string).FullName),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("input-bound-required-string"))
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("input-unbound-required")),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "div",
|
||||
typeName: "DivTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("boundbool")
|
||||
.PropertyName("BoundBoolProp")
|
||||
.TypeName(typeof(bool).FullName),
|
||||
builder => builder
|
||||
.Name("booldict")
|
||||
.PropertyName("BoolDictProp")
|
||||
.TypeName("System.Collections.Generic.IDictionary<string, bool>")
|
||||
.AsDictionaryAttribute("booldict-prefix-", typeof(bool).FullName),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TagHelperDescriptor> DynamicAttributeTagHelpers_Descriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("bound")
|
||||
.PropertyName("Bound")
|
||||
.TypeName(typeof(string).FullName)
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TagHelperDescriptor> DuplicateTargetTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
var typePropertyInfo = typeof(TestType).GetRuntimeProperty("Type");
|
||||
var checkedPropertyInfo = typeof(TestType).GetRuntimeProperty("Checked");
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", typePropertyInfo),
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", checkedPropertyInfo),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("type")),
|
||||
builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("checked"))
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", typePropertyInfo),
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", checkedPropertyInfo),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("type")),
|
||||
builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("checked"))
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TagHelperDescriptor> AttributeTargetingTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
var inputTypePropertyInfo = typeof(TestType).GetRuntimeProperty("Type");
|
||||
var inputCheckedPropertyInfo = typeof(TestType).GetRuntimeProperty("Checked");
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "p",
|
||||
typeName: "TestNamespace.PTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("class")),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("type")),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper2",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", inputCheckedPropertyInfo),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("type"))
|
||||
.RequireAttributeDescriptor(attribute => attribute.Name("checked")),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder.RequireAttributeDescriptor(attribute => attribute.Name("catchAll")),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TagHelperDescriptor> PrefixedAttributeTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper1",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("int-prefix-grabber")
|
||||
.PropertyName("IntProperty")
|
||||
.TypeName(typeof(int).FullName),
|
||||
builder => builder
|
||||
.Name("int-dictionary")
|
||||
.PropertyName("IntDictionaryProperty")
|
||||
.TypeName("System.Collections.Generic.IDictionary<string, int>")
|
||||
.AsDictionaryAttribute("int-prefix-", typeof(int).FullName),
|
||||
builder => builder
|
||||
.Name("string-prefix-grabber")
|
||||
.PropertyName("StringProperty")
|
||||
.TypeName(typeof(string).FullName),
|
||||
builder => builder
|
||||
.Name("string-dictionary")
|
||||
.PropertyName("StringDictionaryProperty")
|
||||
.TypeName("Namespace.DictionaryWithoutParameterlessConstructor<string, string>")
|
||||
.AsDictionaryAttribute("string-prefix-", typeof(string).FullName),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper2",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.Name("int-dictionary")
|
||||
.PropertyName("IntDictionaryProperty")
|
||||
.TypeName(typeof(int).FullName)
|
||||
.AsDictionaryAttribute("int-prefix-", typeof(int).FullName),
|
||||
builder => builder
|
||||
.Name("string-dictionary")
|
||||
.PropertyName("StringDictionaryProperty")
|
||||
.TypeName("Namespace.DictionaryWithoutParameterlessConstructor<string, string>")
|
||||
.AsDictionaryAttribute("string-prefix-", typeof(string).FullName),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TagHelperDescriptor> TagHelpersInSectionDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
var propertyInfo = typeof(TestType).GetRuntimeProperty("BoundProperty");
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "MyTagHelper",
|
||||
typeName: "TestNamespace.MyTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "BoundProperty", propertyInfo),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "NestedTagHelper",
|
||||
typeName: "TestNamespace.NestedTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TagHelperDescriptor> DefaultPAndInputTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
var pAgePropertyInfo = typeof(TestType).GetRuntimeProperty("Age");
|
||||
var inputTypePropertyInfo = typeof(TestType).GetRuntimeProperty("Type");
|
||||
var checkedPropertyInfo = typeof(TestType).GetRuntimeProperty("Checked");
|
||||
|
||||
return new[]
|
||||
{
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "p",
|
||||
typeName: "TestNamespace.PTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "age", pAgePropertyInfo),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder.RequireTagStructure(TagStructure.NormalOrSelfClosing)
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleDescriptorBuilder>[]
|
||||
{
|
||||
builder => builder.RequireTagStructure(TagStructure.WithoutEndTag)
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper2",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<BoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", checkedPropertyInfo),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor CreateTagHelperDescriptor(
|
||||
string tagName,
|
||||
string typeName,
|
||||
string assemblyName,
|
||||
IEnumerable<Action<BoundAttributeDescriptorBuilder>> attributes = null,
|
||||
IEnumerable<Action<TagMatchingRuleDescriptorBuilder>> ruleBuilders = null)
|
||||
{
|
||||
var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||
builder.TypeName(typeName);
|
||||
|
||||
if (attributes != null)
|
||||
{
|
||||
foreach (var attributeBuilder in attributes)
|
||||
{
|
||||
builder.BoundAttributeDescriptor(attributeBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
if (ruleBuilders != null)
|
||||
{
|
||||
foreach (var ruleBuilder in ruleBuilders)
|
||||
{
|
||||
builder.TagMatchingRuleDescriptor(innerRuleBuilder =>
|
||||
{
|
||||
innerRuleBuilder.RequireTagName(tagName);
|
||||
ruleBuilder(innerRuleBuilder);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.TagMatchingRuleDescriptor(ruleBuilder => ruleBuilder.RequireTagName(tagName));
|
||||
}
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private static void BuildBoundAttributeDescriptorFromPropertyInfo(
|
||||
BoundAttributeDescriptorBuilder builder,
|
||||
string name,
|
||||
PropertyInfo propertyInfo)
|
||||
{
|
||||
builder
|
||||
.Name(name)
|
||||
.PropertyName(propertyInfo.Name)
|
||||
.TypeName(propertyInfo.PropertyType.FullName);
|
||||
|
||||
if (propertyInfo.PropertyType.GetTypeInfo().IsEnum)
|
||||
{
|
||||
builder.AsEnum();
|
||||
}
|
||||
}
|
||||
|
||||
private class TestType
|
||||
{
|
||||
public int Age { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public bool Checked { get; set; }
|
||||
|
||||
public string BoundProperty { get; set; }
|
||||
}
|
||||
|
||||
public enum MyEnum
|
||||
{
|
||||
MyValue,
|
||||
MySecondValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
// 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 Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||
{
|
||||
public class DefaultRazorIntermediateNodeBuilderTest
|
||||
{
|
||||
[Fact]
|
||||
public void Ctor_CreatesEmptyBuilder()
|
||||
{
|
||||
// Arrange & Act
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
var current = builder.Current;
|
||||
|
||||
// Assert
|
||||
Assert.Null(current);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Push_WhenEmpty_AddsNode()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
var node = new BasicIntermediateNode();
|
||||
|
||||
// Act
|
||||
builder.Push(node);
|
||||
|
||||
// Assert
|
||||
Assert.Same(node, builder.Current);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Push_WhenNonEmpty_SetsUpChild()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
|
||||
var parent = new BasicIntermediateNode();
|
||||
builder.Push(parent);
|
||||
|
||||
var node = new BasicIntermediateNode();
|
||||
|
||||
// Act
|
||||
builder.Push(node);
|
||||
|
||||
// Assert
|
||||
Assert.Same(node, builder.Current);
|
||||
Assert.Collection(parent.Children, n => Assert.Same(node, n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pop_ThrowsWhenEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
() => builder.Pop(),
|
||||
"The 'Pop' operation is not valid when the builder is empty.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pop_SingleNodeDepth_RemovesAndReturnsNode()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
|
||||
var node = new BasicIntermediateNode();
|
||||
builder.Push(node);
|
||||
|
||||
// Act
|
||||
var result = builder.Pop();
|
||||
|
||||
// Assert
|
||||
Assert.Same(node, result);
|
||||
Assert.Null(builder.Current);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pop_MultipleNodeDepth_RemovesAndReturnsNode()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
|
||||
var parent = new BasicIntermediateNode();
|
||||
builder.Push(parent);
|
||||
|
||||
var node = new BasicIntermediateNode();
|
||||
builder.Push(node);
|
||||
|
||||
// Act
|
||||
var result = builder.Pop();
|
||||
|
||||
// Assert
|
||||
Assert.Same(node, result);
|
||||
Assert.Same(parent, builder.Current);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_AddsToChildrenAndSetsParent()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
|
||||
var parent = new BasicIntermediateNode();
|
||||
builder.Push(parent);
|
||||
|
||||
var node = new BasicIntermediateNode();
|
||||
|
||||
// Act
|
||||
builder.Add(node);
|
||||
|
||||
// Assert
|
||||
Assert.Same(parent, builder.Current);
|
||||
Assert.Collection(parent.Children, n => Assert.Same(node, n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Insert_AddsToChildren_EmptyCollection()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
|
||||
var parent = new BasicIntermediateNode();
|
||||
builder.Push(parent);
|
||||
|
||||
var node = new BasicIntermediateNode();
|
||||
|
||||
// Act
|
||||
builder.Insert(0, node);
|
||||
|
||||
// Assert
|
||||
Assert.Same(parent, builder.Current);
|
||||
Assert.Collection(parent.Children, n => Assert.Same(node, n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Insert_AddsToChildren_NonEmpyCollection()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
|
||||
var parent = new BasicIntermediateNode();
|
||||
builder.Push(parent);
|
||||
|
||||
var child = new BasicIntermediateNode();
|
||||
builder.Add(child);
|
||||
|
||||
var node = new BasicIntermediateNode();
|
||||
|
||||
// Act
|
||||
builder.Insert(0, node);
|
||||
|
||||
// Assert
|
||||
Assert.Same(parent, builder.Current);
|
||||
Assert.Collection(parent.Children, n => Assert.Same(node, n), n => Assert.Same(child, n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Insert_AddsToChildren_NonEmpyCollection_AtEnd()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
|
||||
var parent = new BasicIntermediateNode();
|
||||
builder.Push(parent);
|
||||
|
||||
var child = new BasicIntermediateNode();
|
||||
builder.Add(child);
|
||||
|
||||
var node = new BasicIntermediateNode();
|
||||
|
||||
// Act
|
||||
builder.Insert(1, node);
|
||||
|
||||
// Assert
|
||||
Assert.Same(parent, builder.Current);
|
||||
Assert.Collection(parent.Children, n => Assert.Same(child, n), n => Assert.Same(node, n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_PopsMultipleLevels()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
|
||||
var document = new DocumentIntermediateNode();
|
||||
builder.Push(document);
|
||||
|
||||
var node = new BasicIntermediateNode();
|
||||
builder.Push(node);
|
||||
|
||||
// Act
|
||||
var result = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.Same(document, result);
|
||||
Assert.Null(builder.Current);
|
||||
}
|
||||
|
||||
private class BasicIntermediateNode : IntermediateNode
|
||||
{
|
||||
public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection();
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||
{
|
||||
public class DocumentIntermediateNodeExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void FindPrimaryClass_FindsClassWithAnnotation()
|
||||
{
|
||||
// Arrange
|
||||
var document = new DocumentIntermediateNode();
|
||||
var @class = new ClassDeclarationIntermediateNode();
|
||||
@class.Annotations[CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass;
|
||||
|
||||
var builder = IntermediateNodeBuilder.Create(document);
|
||||
builder.Add(@class);
|
||||
|
||||
// Act
|
||||
var result = document.FindPrimaryClass();
|
||||
|
||||
// Assert
|
||||
Assert.Same(@class, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindPrimaryMethod_FindsMethodWithAnnotation()
|
||||
{
|
||||
// Arrange
|
||||
var document = new DocumentIntermediateNode();
|
||||
var method = new MethodDeclarationIntermediateNode();
|
||||
method.Annotations[CommonAnnotations.PrimaryMethod] = CommonAnnotations.PrimaryMethod;
|
||||
|
||||
var builder = IntermediateNodeBuilder.Create(document);
|
||||
builder.Add(method);
|
||||
|
||||
// Act
|
||||
var result = document.FindPrimaryMethod();
|
||||
|
||||
// Assert
|
||||
Assert.Same(method, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindPrimaryNamespace_FindsNamespaceWithAnnotation()
|
||||
{
|
||||
// Arrange
|
||||
var document = new DocumentIntermediateNode();
|
||||
var @namespace = new NamespaceDeclarationIntermediateNode();
|
||||
@namespace.Annotations[CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace;
|
||||
|
||||
var builder = IntermediateNodeBuilder.Create(document);
|
||||
builder.Add(@namespace);
|
||||
|
||||
// Act
|
||||
var result = document.FindPrimaryNamespace();
|
||||
|
||||
// Assert
|
||||
Assert.Same(@namespace, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindDirectiveReferences_FindsMatchingDirectives()
|
||||
{
|
||||
// Arrange
|
||||
var directive = DirectiveDescriptor.CreateSingleLineDirective("test");
|
||||
var directive2 = DirectiveDescriptor.CreateSingleLineDirective("test");
|
||||
|
||||
var document = new DocumentIntermediateNode();
|
||||
var @namespace = new NamespaceDeclarationIntermediateNode();
|
||||
|
||||
var builder = IntermediateNodeBuilder.Create(document);
|
||||
builder.Push(@namespace);
|
||||
|
||||
var match1 = new DirectiveIntermediateNode()
|
||||
{
|
||||
Directive = directive,
|
||||
};
|
||||
builder.Add(match1);
|
||||
|
||||
var nonMatch = new DirectiveIntermediateNode()
|
||||
{
|
||||
Directive = directive2,
|
||||
};
|
||||
builder.Add(nonMatch);
|
||||
|
||||
var match2 = new DirectiveIntermediateNode()
|
||||
{
|
||||
Directive = directive,
|
||||
};
|
||||
builder.Add(match2);
|
||||
|
||||
// Act
|
||||
var results = document.FindDirectiveReferences(directive);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
results,
|
||||
r =>
|
||||
{
|
||||
Assert.Same(@namespace, r.Parent);
|
||||
Assert.Same(match1, r.Node);
|
||||
},
|
||||
r =>
|
||||
{
|
||||
Assert.Same(@namespace, r.Parent);
|
||||
Assert.Same(match2, r.Node);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||
{
|
||||
// These tests cover the methods on ExtensionIntermediateNode that are used to implement visitors
|
||||
// that special case an extension node.
|
||||
public class ExtensionIntermediateNodeTest
|
||||
{
|
||||
[Fact]
|
||||
public void Accept_CallsStandardVisitExtension_ForStandardVisitor()
|
||||
{
|
||||
// Arrange
|
||||
var node = new TestExtensionIntermediateNode();
|
||||
var visitor = new StandardVisitor();
|
||||
|
||||
// Act
|
||||
node.Accept(visitor);
|
||||
|
||||
// Assert
|
||||
Assert.True(visitor.WasStandardMethodCalled);
|
||||
Assert.False(visitor.WasSpecificMethodCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accept_CallsSpecialVisitExtension_ForSpecialVisitor()
|
||||
{
|
||||
// Arrange
|
||||
var node = new TestExtensionIntermediateNode();
|
||||
var visitor = new SpecialVisitor();
|
||||
|
||||
// Act
|
||||
node.Accept(visitor);
|
||||
|
||||
// Assert
|
||||
Assert.False(visitor.WasStandardMethodCalled);
|
||||
Assert.True(visitor.WasSpecificMethodCalled);
|
||||
}
|
||||
|
||||
private class TestExtensionIntermediateNode : ExtensionIntermediateNode
|
||||
{
|
||||
public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly;
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
{
|
||||
// This is the standard visitor boilerplate for an extension node.
|
||||
AcceptExtensionNode<TestExtensionIntermediateNode>(this, visitor);
|
||||
}
|
||||
|
||||
public override void WriteNode(CodeTarget target, CodeRenderingContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class StandardVisitor : IntermediateNodeVisitor
|
||||
{
|
||||
public bool WasStandardMethodCalled { get; private set; }
|
||||
public bool WasSpecificMethodCalled { get; private set; }
|
||||
|
||||
public override void VisitExtension(ExtensionIntermediateNode node)
|
||||
{
|
||||
WasStandardMethodCalled = true;
|
||||
}
|
||||
|
||||
public void VisitExtension(TestExtensionIntermediateNode node)
|
||||
{
|
||||
WasSpecificMethodCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private class SpecialVisitor : IntermediateNodeVisitor, IExtensionIntermediateNodeVisitor<TestExtensionIntermediateNode>
|
||||
{
|
||||
public bool WasStandardMethodCalled { get; private set; }
|
||||
public bool WasSpecificMethodCalled { get; private set; }
|
||||
|
||||
public override void VisitExtension(ExtensionIntermediateNode node)
|
||||
{
|
||||
WasStandardMethodCalled = true;
|
||||
}
|
||||
|
||||
public void VisitExtension(TestExtensionIntermediateNode node)
|
||||
{
|
||||
WasSpecificMethodCalled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,510 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||
{
|
||||
public class IntermediateNodeReferenceTest
|
||||
{
|
||||
[Fact]
|
||||
public void InsertAfter_SingleNode_AddsNodeAfterNode()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
var node2 = new BasicIntermediateNode("Node2");
|
||||
var node3 = new BasicIntermediateNode("Node3");
|
||||
|
||||
parent.Children.Add(node1);
|
||||
parent.Children.Add(node3);
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act
|
||||
reference.InsertAfter(node2);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { node1, node2, node3, }, parent.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertAfter_SingleNode_AddsNodeAfterNode_AtEnd()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
var node2 = new BasicIntermediateNode("Node2");
|
||||
var node3 = new BasicIntermediateNode("Node3");
|
||||
|
||||
parent.Children.Add(node1);
|
||||
parent.Children.Add(node2);
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node2);
|
||||
|
||||
// Act
|
||||
reference.InsertAfter(node3);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { node1, node2, node3, }, parent.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertAfter_MultipleNodes_AddsNodesAfterNode()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
var node2 = new BasicIntermediateNode("Node2");
|
||||
var node3 = new BasicIntermediateNode("Node3");
|
||||
var node4 = new BasicIntermediateNode("Node4");
|
||||
|
||||
parent.Children.Add(node1);
|
||||
parent.Children.Add(node4);
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act
|
||||
reference.InsertAfter(new[] { node2, node3 });
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { node1, node2, node3, node4, }, parent.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertAfter_MultipleNodes_AddsNodesAfterNode_AtEnd()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
var node2 = new BasicIntermediateNode("Node2");
|
||||
var node3 = new BasicIntermediateNode("Node3");
|
||||
var node4 = new BasicIntermediateNode("Node4");
|
||||
|
||||
parent.Children.Add(node1);
|
||||
parent.Children.Add(node2);
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node2);
|
||||
|
||||
// Act
|
||||
reference.InsertAfter(new[] { node3, node4 });
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { node1, node2, node3, node4, }, parent.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertBefore_SingleNode_AddsNodeBeforeNode()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
var node2 = new BasicIntermediateNode("Node2");
|
||||
var node3 = new BasicIntermediateNode("Node3");
|
||||
|
||||
parent.Children.Add(node1);
|
||||
parent.Children.Add(node3);
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node3);
|
||||
|
||||
// Act
|
||||
reference.InsertBefore(node2);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { node1, node2, node3, }, parent.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertBefore_SingleNode_AddsNodeBeforeNode_AtBeginning()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
var node2 = new BasicIntermediateNode("Node2");
|
||||
var node3 = new BasicIntermediateNode("Node3");
|
||||
|
||||
parent.Children.Add(node2);
|
||||
parent.Children.Add(node3);
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node2);
|
||||
|
||||
// Act
|
||||
reference.InsertBefore(node1);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { node1, node2, node3, }, parent.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertBefore_MultipleNodes_AddsNodesBeforeNode()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
var node2 = new BasicIntermediateNode("Node2");
|
||||
var node3 = new BasicIntermediateNode("Node3");
|
||||
var node4 = new BasicIntermediateNode("Node4");
|
||||
|
||||
parent.Children.Add(node1);
|
||||
parent.Children.Add(node4);
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node4);
|
||||
|
||||
// Act
|
||||
reference.InsertBefore(new[] { node2, node3 });
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { node1, node2, node3, node4, }, parent.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertAfter_MultipleNodes_AddsNodesBeforeNode_AtBeginning()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
var node2 = new BasicIntermediateNode("Node2");
|
||||
var node3 = new BasicIntermediateNode("Node3");
|
||||
var node4 = new BasicIntermediateNode("Node4");
|
||||
|
||||
parent.Children.Add(node3);
|
||||
parent.Children.Add(node4);
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node3);
|
||||
|
||||
// Act
|
||||
reference.InsertBefore(new[] { node1, node2 });
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { node1, node2, node3, node4, }, parent.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Remove_RemovesNode()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
var node2 = new BasicIntermediateNode("Node2");
|
||||
var node3 = new BasicIntermediateNode("Node3");
|
||||
|
||||
parent.Children.Add(node1);
|
||||
parent.Children.Add(node3);
|
||||
parent.Children.Add(node2);
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node3);
|
||||
|
||||
// Act
|
||||
reference.Remove();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { node1, node2,}, parent.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Replace_ReplacesNode()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
var node2 = new BasicIntermediateNode("Node2");
|
||||
var node3 = new BasicIntermediateNode("Node3");
|
||||
var node4 = new BasicIntermediateNode("Node4");
|
||||
|
||||
parent.Children.Add(node1);
|
||||
parent.Children.Add(node4);
|
||||
parent.Children.Add(node3);
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node4);
|
||||
|
||||
// Act
|
||||
reference.Replace(node2);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { node1, node2, node3, }, parent.Children);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertAfter_SingleNode_ThrowsForReferenceNotInitialized()
|
||||
{
|
||||
// Arrange
|
||||
var reference = new IntermediateNodeReference();
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertAfter(new BasicIntermediateNode("_")));
|
||||
Assert.Equal("The reference is invalid. References initialized with the default constructor cannot modify nodes.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertAfter_MulipleNodes_ThrowsForReferenceNotInitialized()
|
||||
{
|
||||
// Arrange
|
||||
var reference = new IntermediateNodeReference();
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertAfter(new[] { new BasicIntermediateNode("_") }));
|
||||
Assert.Equal("The reference is invalid. References initialized with the default constructor cannot modify nodes.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertBefore_SingleNode_ThrowsForReferenceNotInitialized()
|
||||
{
|
||||
// Arrange
|
||||
var reference = new IntermediateNodeReference();
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertBefore(new BasicIntermediateNode("_")));
|
||||
Assert.Equal("The reference is invalid. References initialized with the default constructor cannot modify nodes.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertBefore_MulipleNodes_ThrowsForReferenceNotInitialized()
|
||||
{
|
||||
// Arrange
|
||||
var reference = new IntermediateNodeReference();
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertBefore(new[] { new BasicIntermediateNode("_") }));
|
||||
Assert.Equal("The reference is invalid. References initialized with the default constructor cannot modify nodes.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Remove_ThrowsForReferenceNotInitialized()
|
||||
{
|
||||
// Arrange
|
||||
var reference = new IntermediateNodeReference();
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.Remove());
|
||||
Assert.Equal("The reference is invalid. References initialized with the default constructor cannot modify nodes.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Replace_ThrowsForReferenceNotInitialized()
|
||||
{
|
||||
// Arrange
|
||||
var reference = new IntermediateNodeReference();
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.Replace(new BasicIntermediateNode("_")));
|
||||
Assert.Equal("The reference is invalid. References initialized with the default constructor cannot modify nodes.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertAfter_SingleNode_ThrowsForReadOnlyCollection()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent", IntermediateNodeCollection.ReadOnly);
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertAfter(new BasicIntermediateNode("_")));
|
||||
Assert.Equal("The node 'Parent' has a read-only child collection and cannot be modified.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertAfter_MulipleNodes_ThrowsForReadOnlyCollection()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent", IntermediateNodeCollection.ReadOnly);
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertAfter(new[] { new BasicIntermediateNode("_") }));
|
||||
Assert.Equal("The node 'Parent' has a read-only child collection and cannot be modified.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertBefore_SingleNode_ThrowsForReadOnlyCollection()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent", IntermediateNodeCollection.ReadOnly);
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertBefore(new BasicIntermediateNode("_")));
|
||||
Assert.Equal("The node 'Parent' has a read-only child collection and cannot be modified.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertBefore_MulipleNodes_ThrowsForReadOnlyCollection()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent", IntermediateNodeCollection.ReadOnly);
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertBefore(new[] { new BasicIntermediateNode("_") }));
|
||||
Assert.Equal("The node 'Parent' has a read-only child collection and cannot be modified.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Remove_ThrowsForReadOnlyCollection()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent", IntermediateNodeCollection.ReadOnly);
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.Remove());
|
||||
Assert.Equal("The node 'Parent' has a read-only child collection and cannot be modified.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Replace_ThrowsForReadOnlyCollection()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent", IntermediateNodeCollection.ReadOnly);
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.Replace(new BasicIntermediateNode("_")));
|
||||
Assert.Equal("The node 'Parent' has a read-only child collection and cannot be modified.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertAfter_SingleNode_ThrowsForNodeNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertAfter(new BasicIntermediateNode("_")));
|
||||
Assert.Equal("The reference is invalid. The node 'Node1' could not be found as a child of 'Parent'.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertAfter_MulipleNodes_ThrowsForNodeNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertAfter(new[] { new BasicIntermediateNode("_") }));
|
||||
Assert.Equal("The reference is invalid. The node 'Node1' could not be found as a child of 'Parent'.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertBefore_SingleNode_ThrowsForNodeNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertBefore(new BasicIntermediateNode("_")));
|
||||
Assert.Equal("The reference is invalid. The node 'Node1' could not be found as a child of 'Parent'.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InsertBefore_MulipleNodes_ThrowsForNodeNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.InsertBefore(new[] { new BasicIntermediateNode("_") }));
|
||||
Assert.Equal("The reference is invalid. The node 'Node1' could not be found as a child of 'Parent'.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Remove_ThrowsForNodeNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.Remove());
|
||||
Assert.Equal("The reference is invalid. The node 'Node1' could not be found as a child of 'Parent'.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Replace_ThrowsForNodeNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var parent = new BasicIntermediateNode("Parent");
|
||||
|
||||
var node1 = new BasicIntermediateNode("Node1");
|
||||
|
||||
var reference = new IntermediateNodeReference(parent, node1);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => reference.Replace(new BasicIntermediateNode("_")));
|
||||
Assert.Equal("The reference is invalid. The node 'Node1' could not be found as a child of 'Parent'.", exception.Message);
|
||||
}
|
||||
|
||||
private class BasicIntermediateNode : IntermediateNode
|
||||
{
|
||||
public BasicIntermediateNode(string name)
|
||||
: this(name, new IntermediateNodeCollection())
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public BasicIntermediateNode(string name, IntermediateNodeCollection children)
|
||||
{
|
||||
Name = name;
|
||||
Children = children;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public override IntermediateNodeCollection Children { get; }
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override string ToString() => Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||
{
|
||||
public class IntermediateNodeWalkerTest
|
||||
{
|
||||
[Fact]
|
||||
public void IntermediateNodeWalker_Visit_TraversesEntireGraph()
|
||||
{
|
||||
// Arrange
|
||||
var walker = new DerivedIntermediateNodeWalker();
|
||||
|
||||
var nodes = new IntermediateNode[]
|
||||
{
|
||||
new BasicIntermediateNode("Root"),
|
||||
new BasicIntermediateNode("Root->A"),
|
||||
new BasicIntermediateNode("Root->B"),
|
||||
new BasicIntermediateNode("Root->B->1"),
|
||||
new BasicIntermediateNode("Root->B->2"),
|
||||
new BasicIntermediateNode("Root->C"),
|
||||
};
|
||||
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
builder.Push(nodes[0]);
|
||||
builder.Add(nodes[1]);
|
||||
builder.Push(nodes[2]);
|
||||
builder.Add(nodes[3]);
|
||||
builder.Add(nodes[4]);
|
||||
builder.Pop();
|
||||
builder.Add(nodes[5]);
|
||||
|
||||
var root = builder.Pop();
|
||||
|
||||
// Act
|
||||
walker.Visit(root);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(nodes, walker.Visited.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IntermediateNodeWalker_Visit_SetsParentAndAncestors()
|
||||
{
|
||||
// Arrange
|
||||
var walker = new DerivedIntermediateNodeWalker();
|
||||
|
||||
var nodes = new IntermediateNode[]
|
||||
{
|
||||
new BasicIntermediateNode("Root"),
|
||||
new BasicIntermediateNode("Root->A"),
|
||||
new BasicIntermediateNode("Root->B"),
|
||||
new BasicIntermediateNode("Root->B->1"),
|
||||
new BasicIntermediateNode("Root->B->2"),
|
||||
new BasicIntermediateNode("Root->C"),
|
||||
};
|
||||
|
||||
var ancestors = new Dictionary<string, string[]>()
|
||||
{
|
||||
{ "Root", new string[]{ } },
|
||||
{ "Root->A", new string[] { "Root" } },
|
||||
{ "Root->B", new string[] { "Root" } },
|
||||
{ "Root->B->1", new string[] { "Root->B", "Root" } },
|
||||
{ "Root->B->2", new string[] { "Root->B", "Root" } },
|
||||
{ "Root->C", new string[] { "Root" } },
|
||||
};
|
||||
|
||||
walker.OnVisiting = (n) =>
|
||||
{
|
||||
Assert.Equal(ancestors[((BasicIntermediateNode)n).Name], walker.Ancestors.Cast<BasicIntermediateNode>().Select(b => b.Name));
|
||||
Assert.Equal(ancestors[((BasicIntermediateNode)n).Name].FirstOrDefault(), ((BasicIntermediateNode)walker.Parent)?.Name);
|
||||
};
|
||||
|
||||
var builder = new DefaultRazorIntermediateNodeBuilder();
|
||||
builder.Push(nodes[0]);
|
||||
builder.Add(nodes[1]);
|
||||
builder.Push(nodes[2]);
|
||||
builder.Add(nodes[3]);
|
||||
builder.Add(nodes[4]);
|
||||
builder.Pop();
|
||||
builder.Add(nodes[5]);
|
||||
|
||||
var root = builder.Pop();
|
||||
|
||||
// Act & Assert
|
||||
walker.Visit(root);
|
||||
}
|
||||
|
||||
private class DerivedIntermediateNodeWalker : IntermediateNodeWalker
|
||||
{
|
||||
public new IReadOnlyList<IntermediateNode> Ancestors => base.Ancestors;
|
||||
|
||||
public new IntermediateNode Parent => base.Parent;
|
||||
|
||||
public List<IntermediateNode> Visited { get; } = new List<IntermediateNode>();
|
||||
|
||||
public Action<IntermediateNode> OnVisiting { get; set; }
|
||||
|
||||
public override void VisitDefault(IntermediateNode node)
|
||||
{
|
||||
Visited.Add(node);
|
||||
|
||||
OnVisiting?.Invoke(node);
|
||||
base.VisitDefault(node);
|
||||
}
|
||||
|
||||
public virtual void VisitBasic(BasicIntermediateNode node)
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
}
|
||||
|
||||
private class BasicIntermediateNode : IntermediateNode
|
||||
{
|
||||
public BasicIntermediateNode(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection();
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
{
|
||||
((DerivedIntermediateNodeWalker)visitor).VisitBasic(this);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Test
|
||||
{
|
||||
public class LargeTextSourceDocumentTest
|
||||
{
|
||||
private const int ChunkTestLength = 10;
|
||||
|
||||
[Fact]
|
||||
public void GetChecksum_ReturnsCopiedChecksum()
|
||||
{
|
||||
// Arrange
|
||||
var contentString = "Hello World";
|
||||
var stream = TestRazorSourceDocument.CreateStreamContent(contentString);
|
||||
var reader = new StreamReader(stream, detectEncodingFromByteOrderMarks: true);
|
||||
var document = new LargeTextSourceDocument(reader, 5, Encoding.UTF8, RazorSourceDocumentProperties.Default);
|
||||
|
||||
// Act
|
||||
var firstChecksum = document.GetChecksum();
|
||||
var secondChecksum = document.GetChecksum();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(firstChecksum, secondChecksum);
|
||||
Assert.NotSame(firstChecksum, secondChecksum);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetChecksum_ComputesCorrectChecksum_UTF8()
|
||||
{
|
||||
// Arrange
|
||||
var contentString = "Hello World";
|
||||
var stream = TestRazorSourceDocument.CreateStreamContent(contentString);
|
||||
var reader = new StreamReader(stream, detectEncodingFromByteOrderMarks: true);
|
||||
var document = new LargeTextSourceDocument(reader, 5, Encoding.UTF8, RazorSourceDocumentProperties.Default);
|
||||
var expectedChecksum = new byte[] { 10, 77, 85, 168, 215, 120, 229, 2, 47, 171, 112, 25, 119, 197, 216, 64, 187, 196, 134, 208 };
|
||||
|
||||
// Act
|
||||
var checksum = document.GetChecksum();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedChecksum, checksum);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetChecksum_ComputesCorrectChecksum_UTF32()
|
||||
{
|
||||
// Arrange
|
||||
var contentString = "Hello World";
|
||||
var stream = TestRazorSourceDocument.CreateStreamContent(contentString, Encoding.UTF32);
|
||||
var reader = new StreamReader(stream, detectEncodingFromByteOrderMarks: true);
|
||||
var document = new LargeTextSourceDocument(reader, 5, Encoding.UTF32, RazorSourceDocumentProperties.Default);
|
||||
var expectedChecksum = new byte[] { 108, 172, 130, 171, 42, 19, 155, 176, 211, 80, 224, 121, 169, 133, 25, 134, 48, 228, 199, 141 };
|
||||
|
||||
// Act
|
||||
var checksum = document.GetChecksum();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedChecksum, checksum);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ChunkTestLength - 1)]
|
||||
[InlineData(ChunkTestLength)]
|
||||
[InlineData(ChunkTestLength + 1)]
|
||||
[InlineData(ChunkTestLength * 2 - 1)]
|
||||
[InlineData(ChunkTestLength * 2)]
|
||||
[InlineData(ChunkTestLength * 2 + 1)]
|
||||
public void Indexer_ProvidesCharacterAccessToContent(int contentLength)
|
||||
{
|
||||
// Arrange
|
||||
var content = new char[contentLength];
|
||||
|
||||
for (var i = 0; i < contentLength - 1; i++)
|
||||
{
|
||||
content[i] = 'a';
|
||||
}
|
||||
content[contentLength - 1] = 'b';
|
||||
var contentString = new string(content);
|
||||
|
||||
var stream = TestRazorSourceDocument.CreateStreamContent(new string(content));
|
||||
var reader = new StreamReader(stream, true);
|
||||
var document = new LargeTextSourceDocument(reader, ChunkTestLength, Encoding.UTF8, RazorSourceDocumentProperties.Default);
|
||||
|
||||
// Act
|
||||
var output = new char[contentLength];
|
||||
for (var i = 0; i < document.Length; i++)
|
||||
{
|
||||
output[i] = document[i];
|
||||
}
|
||||
var outputString = new string(output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(contentLength, document.Length);
|
||||
Assert.Equal(contentString, outputString);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("test.cshtml")]
|
||||
[InlineData(null)]
|
||||
public void FilePath(string filePath)
|
||||
{
|
||||
// Arrange
|
||||
var stream = TestRazorSourceDocument.CreateStreamContent("abc");
|
||||
var reader = new StreamReader(stream, true);
|
||||
|
||||
// Act
|
||||
var document = new LargeTextSourceDocument(reader, ChunkTestLength, Encoding.UTF8, new RazorSourceDocumentProperties(filePath: filePath, relativePath: null));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(filePath, document.FilePath);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("test.cshtml")]
|
||||
[InlineData(null)]
|
||||
public void RelativePath(string relativePath)
|
||||
{
|
||||
// Arrange
|
||||
var stream = TestRazorSourceDocument.CreateStreamContent("abc");
|
||||
var reader = new StreamReader(stream, true);
|
||||
|
||||
// Act
|
||||
var document = new LargeTextSourceDocument(reader, ChunkTestLength, Encoding.UTF8, new RazorSourceDocumentProperties(filePath: null, relativePath: relativePath));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(relativePath, document.RelativePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Lines()
|
||||
{
|
||||
// Arrange
|
||||
var stream = TestRazorSourceDocument.CreateStreamContent("abc\ndef\nghi");
|
||||
var reader = new StreamReader(stream, true);
|
||||
|
||||
// Act
|
||||
var document = new LargeTextSourceDocument(reader, ChunkTestLength, Encoding.UTF8, RazorSourceDocumentProperties.Default);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, document.Lines.Count);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", 0, 0, 0)] // Nothing to copy
|
||||
[InlineData("a", 0, 100, 1)] // Destination index different from start
|
||||
[InlineData("j", ChunkTestLength - 1, 0, 1)] // One char just before the chunk limit
|
||||
[InlineData("k", ChunkTestLength, 0, 1)] // One char one the chunk limit
|
||||
[InlineData("l", ChunkTestLength + 1, 0, 1)] // One char just after the chunk limit
|
||||
[InlineData("jk", ChunkTestLength - 1, 0, 2)] // Two char that are on both chunk sides
|
||||
[InlineData("abcdefghijklmnopqrstuvwxy", 0, 100, 25)] // Everything except the last
|
||||
[InlineData("abcdefghijklmnopqrstuvwxyz", 0, 0, 26)] // Copy all
|
||||
[InlineData("xyz", 23, 0, 3)] // The last chars
|
||||
public void CopyTo(string expected, int sourceIndex, int destinationIndex, int count)
|
||||
{
|
||||
// Arrange
|
||||
var stream = TestRazorSourceDocument.CreateStreamContent("abcdefghijklmnopqrstuvwxyz");
|
||||
|
||||
var reader = new StreamReader(stream, true);
|
||||
var document = new LargeTextSourceDocument(reader, ChunkTestLength, Encoding.UTF8, RazorSourceDocumentProperties.Default);
|
||||
|
||||
// Act
|
||||
var destination = new char[1000];
|
||||
document.CopyTo(sourceIndex, destination, destinationIndex, count);
|
||||
|
||||
// Assert
|
||||
var copy = new string(destination, destinationIndex, count);
|
||||
Assert.Equal(expected, copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// 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.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public static class BaselineWriter
|
||||
{
|
||||
private static object baselineLock = new object();
|
||||
|
||||
[Conditional("GENERATE_BASELINES")]
|
||||
public static void WriteBaseline(string baselineFile, string output)
|
||||
{
|
||||
var root = RecursiveFind("Razor.sln", Path.GetFullPath("."));
|
||||
var baselinePath = Path.Combine(root, baselineFile);
|
||||
|
||||
// Serialize writes to minimize contention for file handles and directory access.
|
||||
lock (baselineLock)
|
||||
{
|
||||
// Update baseline
|
||||
using (var stream = File.Open(baselinePath, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
using (var writer = new StreamWriter(stream))
|
||||
{
|
||||
writer.Write(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string RecursiveFind(string path, string start)
|
||||
{
|
||||
var test = Path.Combine(start, path);
|
||||
if (File.Exists(test))
|
||||
{
|
||||
return start;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RecursiveFind(path, new DirectoryInfo(start).Parent.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpAutoCompleteTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void FunctionsDirectiveAutoCompleteAtEOF()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
ParseDocumentTest("@functions{", new[] { FunctionsDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SectionDirectiveAutoCompleteAtEOF()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
ParseDocumentTest("@section Header {", new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerbatimBlockAutoCompleteAtEOF()
|
||||
{
|
||||
ParseDocumentTest("@{");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FunctionsDirectiveAutoCompleteAtStartOfFile()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
ParseDocumentTest("@functions{" + Environment.NewLine + "foo", new[] { FunctionsDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SectionDirectiveAutoCompleteAtStartOfFile()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
ParseDocumentTest("@section Header {" + Environment.NewLine + "<p>Foo</p>", new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerbatimBlockAutoCompleteAtStartOfFile()
|
||||
{
|
||||
ParseDocumentTest("@{" + Environment.NewLine + "<p></p>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,719 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpBlockTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void LocalFunctionsWithRazor()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
@"@{
|
||||
void Foo()
|
||||
{
|
||||
var time = DateTime.Now
|
||||
<strong>Hello the time is @time</strong>
|
||||
}
|
||||
}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LocalFunctionsWithGenerics()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
@"@{
|
||||
void Foo()
|
||||
{
|
||||
<strong>Hello the time is @{ DisplayCount(new List<string>()); }</strong>
|
||||
}
|
||||
|
||||
void DisplayCount<T>(List<T> something)
|
||||
{
|
||||
<text>The count is something.Count</text>
|
||||
}
|
||||
}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedCodeBlockWithCSharpAt()
|
||||
{
|
||||
ParseDocumentTest("@{ if (true) { var val = @x; if (val != 3) { } } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedCodeBlockWithMarkupSetsDotAsMarkup()
|
||||
{
|
||||
ParseDocumentTest("@if (true) { @if(false) { <div>@something.</div> } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BalancingBracketsIgnoresStringLiteralCharactersAndBrackets()
|
||||
{
|
||||
// BalancingBracketsIgnoresStringLiteralCharactersAndBracketsInsideSingleLineComments
|
||||
ParseDocumentTest(@"@if(foo) {
|
||||
// bar } "" baz '
|
||||
zoop();
|
||||
}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedCodeBlockWithAtDoesntCauseError()
|
||||
{
|
||||
ParseDocumentTest("@if (true) { @if(false) { } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BalancingBracketsIgnoresStringLiteralCharactersAndBracketsInsideBlockComments()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
@"@if(foo) {
|
||||
/* bar } "" */ ' baz } '
|
||||
zoop();
|
||||
}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkipsExprThenBalancesBracesIfFirstIdentifierIsForKeyword()
|
||||
{
|
||||
// ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsForKeyword
|
||||
ParseDocumentTest(
|
||||
"@for(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkipsExprThenBalancesBracesIfFirstIdentifierIsForeachKeyword()
|
||||
{
|
||||
// ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsForeachKeyword
|
||||
ParseDocumentTest(
|
||||
"@foreach(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkipsExprThenBalancesBracesIfFirstIdentifierIsWhileKeyword()
|
||||
{
|
||||
// ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsWhileKeyword
|
||||
ParseDocumentTest(
|
||||
"@while(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkipsExprThenBalancesIfFirstIdentifierIsUsingFollowedByParen()
|
||||
{
|
||||
// ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsUsingKeywordFollowedByParen
|
||||
ParseDocumentTest(
|
||||
"@using(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsUsingsNestedWithinOtherBlocks()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@if(foo) { using(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkipsExprThenBalancesBracesIfFirstIdentifierIsIfKeywordWithNoElseBranches()
|
||||
{
|
||||
// ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsIfKeywordWithNoElseBranches
|
||||
ParseDocumentTest(
|
||||
"@if(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllowsEmptyBlockStatement()
|
||||
{
|
||||
ParseDocumentTest("@if(false) { }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesParenBalancingAtEOF()
|
||||
{
|
||||
ParseDocumentTest("@Html.En(code()");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsBlockCommentBetweenIfAndElseClause()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@if(foo) { bar(); } /* Foo */ /* Bar */ else { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsRazorCommentBetweenIfAndElseClause()
|
||||
{
|
||||
RunRazorCommentBetweenClausesTest(
|
||||
"@if(foo) { bar(); } ", " else { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsBlockCommentBetweenElseIfAndElseClause()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@if(foo) { bar(); } else if(bar) { baz(); } /* Foo */ /* Bar */ else { biz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsRazorCommentBetweenElseIfAndElseClause()
|
||||
{
|
||||
RunRazorCommentBetweenClausesTest(
|
||||
"@if(foo) { bar(); } else if(bar) { baz(); } ", " else { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsBlockCommentBetweenIfAndElseIfClause()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"if(foo) { bar(); } /* Foo */ /* Bar */ else if(bar) { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsRazorCommentBetweenIfAndElseIfClause()
|
||||
{
|
||||
RunRazorCommentBetweenClausesTest("@if(foo) { bar(); } ", " else if(bar) { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsLineCommentBetweenIfAndElseClause()
|
||||
{
|
||||
ParseDocumentTest(@"@if(foo) { bar(); }
|
||||
// Foo
|
||||
// Bar
|
||||
else { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsLineCommentBetweenElseIfAndElseClause()
|
||||
{
|
||||
ParseDocumentTest(@"@if(foo) { bar(); } else if(bar) { baz(); }
|
||||
// Foo
|
||||
// Bar
|
||||
else { biz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsLineCommentBetweenIfAndElseIfClause()
|
||||
{
|
||||
ParseDocumentTest(@"@if(foo) { bar(); }
|
||||
// Foo
|
||||
// Bar
|
||||
else if(bar) { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesElseIfBranchesOfIfStatement()
|
||||
{
|
||||
const string ifStatement = @"@if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
||||
Debug.WriteLine(@""foo } bar"");
|
||||
}";
|
||||
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
||||
Debug.WriteLine(@""bar } baz"");
|
||||
}";
|
||||
const string document = ifStatement + elseIfBranch;
|
||||
|
||||
ParseDocumentTest(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesMultipleElseIfBranchesOfIfStatement()
|
||||
{
|
||||
const string ifStatement = @"@if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
||||
Debug.WriteLine(@""foo } bar"");
|
||||
}";
|
||||
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
||||
Debug.WriteLine(@""bar } baz"");
|
||||
}";
|
||||
const string document = ifStatement + elseIfBranch + elseIfBranch + elseIfBranch + elseIfBranch;
|
||||
ParseDocumentTest(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesMultipleElseIfBranchesOfIfStatementFollowedByOneElseBranch()
|
||||
{
|
||||
const string ifStatement = @"@if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
||||
Debug.WriteLine(@""foo } bar"");
|
||||
}";
|
||||
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
||||
Debug.WriteLine(@""bar } baz"");
|
||||
}";
|
||||
const string elseBranch = @" else { Debug.WriteLine(@""bar } baz""); }";
|
||||
const string document = ifStatement + elseIfBranch + elseIfBranch + elseBranch;
|
||||
|
||||
ParseDocumentTest(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StopsParsingCodeAfterElseBranch()
|
||||
{
|
||||
const string ifStatement = @"@if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
||||
Debug.WriteLine(@""foo } bar"");
|
||||
}";
|
||||
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
||||
Debug.WriteLine(@""bar } baz"");
|
||||
}";
|
||||
const string elseBranch = @" else { Debug.WriteLine(@""bar } baz""); }";
|
||||
const string document = ifStatement + elseIfBranch + elseBranch + elseIfBranch;
|
||||
|
||||
ParseDocumentTest(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StopsParsingIfIfStatementNotFollowedByElse()
|
||||
{
|
||||
const string document = @"@if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
||||
Debug.WriteLine(@""foo } bar"");
|
||||
}";
|
||||
|
||||
ParseDocumentTest(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AcceptsElseIfWithNoCondition()
|
||||
{
|
||||
// We don't want to be a full C# parser - If the else if is missing it's condition, the C# compiler
|
||||
// can handle that, we have all the info we need to keep parsing
|
||||
const string ifBranch = @"@if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
||||
Debug.WriteLine(@""foo } bar"");
|
||||
}";
|
||||
const string elseIfBranch = @" else if { foo(); }";
|
||||
const string document = ifBranch + elseIfBranch;
|
||||
|
||||
ParseDocumentTest(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyParsesDoWhileBlock()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@do { var foo = bar; } while(foo != bar);");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyParsesDoWhileBlockMissingSemicolon()
|
||||
{
|
||||
ParseDocumentTest("@do { var foo = bar; } while(foo != bar)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyParsesDoWhileBlockMissingWhileCondition()
|
||||
{
|
||||
ParseDocumentTest("@do { var foo = bar; } while");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyParsesDoWhileBlockMissingWhileConditionWithSemicolon()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@do { var foo = bar; } while;");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyParsesDoWhileBlockMissingWhileClauseEntirely()
|
||||
{
|
||||
ParseDocumentTest("@do { var foo = bar; } narf;");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsBlockCommentBetweenDoAndWhileClause()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@do { var foo = bar; } /* Foo */ /* Bar */ while(true);");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsLineCommentBetweenDoAndWhileClause()
|
||||
{
|
||||
ParseDocumentTest(@"@do { var foo = bar; }
|
||||
// Foo
|
||||
// Bar
|
||||
while(true);");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsRazorCommentBetweenDoAndWhileClause()
|
||||
{
|
||||
RunRazorCommentBetweenClausesTest(
|
||||
"@do { var foo = bar; } ", " while(true);");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyParsesMarkupInDoWhileBlock()
|
||||
{
|
||||
ParseDocumentTest("@do { var foo = bar; <p>Foo</p> foo++; } while (foo<bar>);");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkipsExprThenBalancesBracesIfFirstIdentifierIsSwitchKeyword()
|
||||
{
|
||||
// ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsSwitchKeyword
|
||||
ParseDocumentTest(@"@switch(foo) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ThenBalancesBracesIfFirstIdentifierIsLockKeyword()
|
||||
{
|
||||
// ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsLockKeyword
|
||||
ParseDocumentTest(
|
||||
"@lock(foo) { Debug.WriteLine(@\"foo } bar\"); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasErrorsIfNamespaceImportMissingSemicolon()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@using Foo.Bar.Baz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasErrorsIfNamespaceAliasMissingSemicolon()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@using Foo.Bar.Baz = FooBarBaz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNamespaceImportWithSemicolonForUsingKeywordIfIsInValidFormat()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@using Foo.Bar.Baz;");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesntCaptureWhitespaceAfterUsing()
|
||||
{
|
||||
ParseDocumentTest("@using Foo ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CapturesNewlineAfterUsing()
|
||||
{
|
||||
ParseDocumentTest($"@using Foo{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNamespaceAliasWithSemicolonForUsingKeywordIfIsInValidFormat()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@using FooBarBaz = FooBarBaz;");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesUsingKeywordAtEOFAndOutputsFileCodeBlock()
|
||||
{
|
||||
ParseDocumentTest("@using ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesSingleLineCommentAtEndOfFile()
|
||||
{
|
||||
const string document = "@foreach(var f in Foo) { // foo bar baz";
|
||||
ParseDocumentTest(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesBlockCommentAtEndOfFile()
|
||||
{
|
||||
const string document = "@foreach(var f in Foo) { /* foo bar baz";
|
||||
ParseDocumentTest(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesSingleSlashAtEndOfFile()
|
||||
{
|
||||
const string document = "@foreach(var f in Foo) { / foo bar baz";
|
||||
ParseDocumentTest(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsBlockCommentBetweenTryAndFinallyClause()
|
||||
{
|
||||
ParseDocumentTest("@try { bar(); } /* Foo */ /* Bar */ finally { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsRazorCommentBetweenTryAndFinallyClause()
|
||||
{
|
||||
RunRazorCommentBetweenClausesTest("@try { bar(); } ", " finally { biz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsBlockCommentBetweenCatchAndFinallyClause()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@try { bar(); } catch(bar) { baz(); } /* Foo */ /* Bar */ finally { biz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsRazorCommentBetweenCatchAndFinallyClause()
|
||||
{
|
||||
RunRazorCommentBetweenClausesTest(
|
||||
"@try { bar(); } catch(bar) { baz(); } ", " finally { biz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsBlockCommentBetweenTryAndCatchClause()
|
||||
{
|
||||
ParseDocumentTest("@try { bar(); } /* Foo */ /* Bar */ catch(bar) { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsRazorCommentBetweenTryAndCatchClause()
|
||||
{
|
||||
RunRazorCommentBetweenClausesTest("@try { bar(); }", " catch(bar) { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsLineCommentBetweenTryAndFinallyClause()
|
||||
{
|
||||
ParseDocumentTest(@"@try { bar(); }
|
||||
// Foo
|
||||
// Bar
|
||||
finally { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsLineCommentBetweenCatchAndFinallyClause()
|
||||
{
|
||||
ParseDocumentTest(@"@try { bar(); } catch(bar) { baz(); }
|
||||
// Foo
|
||||
// Bar
|
||||
finally { biz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsLineCommentBetweenTryAndCatchClause()
|
||||
{
|
||||
ParseDocumentTest(@"@try { bar(); }
|
||||
// Foo
|
||||
// Bar
|
||||
catch(bar) { baz(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsTryStatementWithNoAdditionalClauses()
|
||||
{
|
||||
ParseDocumentTest("@try { var foo = new { } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsMarkupWithinTryClause()
|
||||
{
|
||||
RunSimpleWrappedMarkupTest(
|
||||
prefix: "@try {",
|
||||
markup: " <p>Foo</p> ",
|
||||
suffix: "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsTryStatementWithOneCatchClause()
|
||||
{
|
||||
ParseDocumentTest("@try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsMarkupWithinCatchClause()
|
||||
{
|
||||
RunSimpleWrappedMarkupTest(
|
||||
prefix: "@try { var foo = new { } } catch(Foo Bar Baz) {",
|
||||
markup: " <p>Foo</p> ",
|
||||
suffix: "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsTryStatementWithMultipleCatchClause()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) " +
|
||||
"{ var foo = new { } } catch(Foo Bar Baz) { var foo = new { } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsExceptionLessCatchClauses()
|
||||
{
|
||||
ParseDocumentTest("@try { var foo = new { } } catch { var foo = new { } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsMarkupWithinAdditionalCatchClauses()
|
||||
{
|
||||
RunSimpleWrappedMarkupTest(
|
||||
prefix: "@try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) " +
|
||||
"{ var foo = new { } } catch(Foo Bar Baz) {",
|
||||
markup: " <p>Foo</p> ",
|
||||
suffix: "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsTryStatementWithFinallyClause()
|
||||
{
|
||||
ParseDocumentTest("@try { var foo = new { } } finally { var foo = new { } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsMarkupWithinFinallyClause()
|
||||
{
|
||||
RunSimpleWrappedMarkupTest(
|
||||
prefix: "@try { var foo = new { } } finally {",
|
||||
markup: " <p>Foo</p> ",
|
||||
suffix: "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StopsParsingCatchClausesAfterFinallyBlock()
|
||||
{
|
||||
var content = "@try { var foo = new { } } finally { var foo = new { } }";
|
||||
ParseDocumentTest(content + " catch(Foo Bar Baz) { }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotAllowMultipleFinallyBlocks()
|
||||
{
|
||||
var content = "@try { var foo = new { } } finally { var foo = new { } }";
|
||||
ParseDocumentTest(content + " finally { }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AcceptsTrailingDotIntoImplicitExpressionWhenEmbeddedInCode()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@if(foo) { @foo. }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesExpressionOnSwitchCharacterFollowedByOpenParen()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@if(foo) { @(foo + bar) }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesExpressionOnSwitchCharacterFollowedByIdentifierStart()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@if(foo) { @foo[4].bar() }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TreatsDoubleAtSignAsEscapeSequenceIfAtStatementStart()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@if(foo) { @@class.Foo() }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TreatsAtSignsAfterFirstPairAsPartOfCSharpStatement()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@if(foo) { @@@@class.Foo() }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotParseOnSwitchCharacterNotFollowedByOpenAngleOrColon()
|
||||
{
|
||||
// ParseBlockDoesNotParseMarkupStatementOrExpressionOnSwitchCharacterNotFollowedByOpenAngleOrColon
|
||||
// Arrange
|
||||
ParseDocumentTest("@if(foo) { @\"Foo\".ToString(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsersCanNestRecursively()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@foreach(var c in db.Categories) {" + Environment.NewLine
|
||||
+ " <div>" + Environment.NewLine
|
||||
+ " <h1>@c.Name</h1>" + Environment.NewLine
|
||||
+ " <ul>" + Environment.NewLine
|
||||
+ " @foreach(var p in c.Products) {" + Environment.NewLine
|
||||
+ " <li><a href=\"@Html.ActionUrl(\"Products\", \"Detail\", new { id = p.Id })\">@p.Name</a></li>" + Environment.NewLine
|
||||
+ " }" + Environment.NewLine
|
||||
+ " </ul>" + Environment.NewLine
|
||||
+ " </div>" + Environment.NewLine
|
||||
+ " }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithDoubleTransitionInAttributeValue_DoesNotThrow()
|
||||
{
|
||||
var input = "@{<span foo='@@' />}";
|
||||
ParseDocumentTest(input);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithDoubleTransitionAtEndOfAttributeValue_DoesNotThrow()
|
||||
{
|
||||
var input = "@{<span foo='abc@@' />}";
|
||||
ParseDocumentTest(input);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithDoubleTransitionAtBeginningOfAttributeValue_DoesNotThrow()
|
||||
{
|
||||
var input = "@{<span foo='@@def' />}";
|
||||
ParseDocumentTest(input);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithDoubleTransitionBetweenAttributeValue_DoesNotThrow()
|
||||
{
|
||||
var input = "@{<span foo='abc @@ def' />}";
|
||||
ParseDocumentTest(input);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithDoubleTransitionWithExpressionBlock_DoesNotThrow()
|
||||
{
|
||||
var input = "@{<span foo='@@@(2+3)' bar='@(2+3)@@@DateTime.Now' baz='@DateTime.Now@@' bat='@DateTime.Now @@' zoo='@@@DateTime.Now' />}";
|
||||
ParseDocumentTest(input);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithDoubleTransitionInEmail_DoesNotThrow()
|
||||
{
|
||||
var input = "@{<span foo='abc@def.com abc@@def.com @@' />}";
|
||||
ParseDocumentTest(input);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithDoubleTransitionInRegex_DoesNotThrow()
|
||||
{
|
||||
var input = @"@{<span foo=""/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@@[a-z0-9]([a-z0-9-]*[a-z0-9])?\.([a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i"" />}";
|
||||
ParseDocumentTest(input);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithDoubleTransition_EndOfFile_Throws()
|
||||
{
|
||||
ParseDocumentTest("@{<span foo='@@");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithUnexpectedTransitionsInAttributeValue_Throws()
|
||||
{
|
||||
ParseDocumentTest("@{<span foo='@ @' />}");
|
||||
}
|
||||
|
||||
private void RunRazorCommentBetweenClausesTest(string preComment, string postComment, AcceptedCharactersInternal acceptedCharacters = AcceptedCharactersInternal.Any)
|
||||
{
|
||||
ParseDocumentTest(preComment + "@* Foo *@ @* Bar *@" + postComment);
|
||||
}
|
||||
|
||||
private void RunSimpleWrappedMarkupTest(string prefix, string markup, string suffix)
|
||||
{
|
||||
ParseDocumentTest(prefix + markup + suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.AspNetCore.Razor.Language.Syntax;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Test.Legacy
|
||||
{
|
||||
public class CSharpCodeParserTest
|
||||
{
|
||||
public static TheoryData InvalidTagHelperPrefixData
|
||||
{
|
||||
get
|
||||
{
|
||||
var directiveLocation = new SourceLocation(1, 2, 3);
|
||||
|
||||
RazorDiagnostic InvalidPrefixError(int length, char character, string prefix)
|
||||
{
|
||||
return RazorDiagnosticFactory.CreateParsing_InvalidTagHelperPrefixValue(
|
||||
new SourceSpan(directiveLocation, length), SyntaxConstants.CSharp.TagHelperPrefixKeyword, character, prefix);
|
||||
}
|
||||
|
||||
return new TheoryData<string, SourceLocation, IEnumerable<RazorDiagnostic>>
|
||||
{
|
||||
{
|
||||
"th ",
|
||||
directiveLocation,
|
||||
new[]
|
||||
{
|
||||
InvalidPrefixError(3, ' ', "th "),
|
||||
}
|
||||
},
|
||||
{
|
||||
"th\t",
|
||||
directiveLocation,
|
||||
new[]
|
||||
{
|
||||
InvalidPrefixError(3, '\t', "th\t"),
|
||||
}
|
||||
},
|
||||
{
|
||||
"th" + Environment.NewLine,
|
||||
directiveLocation,
|
||||
new[]
|
||||
{
|
||||
InvalidPrefixError(2 + Environment.NewLine.Length, Environment.NewLine[0], "th" + Environment.NewLine),
|
||||
}
|
||||
},
|
||||
{
|
||||
" th ",
|
||||
directiveLocation,
|
||||
new[]
|
||||
{
|
||||
InvalidPrefixError(4, ' ', " th "),
|
||||
}
|
||||
},
|
||||
{
|
||||
"@",
|
||||
directiveLocation,
|
||||
new[]
|
||||
{
|
||||
InvalidPrefixError(1, '@', "@"),
|
||||
}
|
||||
},
|
||||
{
|
||||
"t@h",
|
||||
directiveLocation,
|
||||
new[]
|
||||
{
|
||||
InvalidPrefixError(3, '@', "t@h"),
|
||||
}
|
||||
},
|
||||
{
|
||||
"!",
|
||||
directiveLocation,
|
||||
new[]
|
||||
{
|
||||
InvalidPrefixError(1, '!', "!"),
|
||||
}
|
||||
},
|
||||
{
|
||||
"!th",
|
||||
directiveLocation,
|
||||
new[]
|
||||
{
|
||||
InvalidPrefixError(3, '!', "!th"),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(InvalidTagHelperPrefixData))]
|
||||
public void ValidateTagHelperPrefix_ValidatesPrefix(
|
||||
string directiveText,
|
||||
SourceLocation directiveLocation,
|
||||
object expectedErrors)
|
||||
{
|
||||
// Arrange
|
||||
var expectedDiagnostics = (IEnumerable<RazorDiagnostic>)expectedErrors;
|
||||
var source = TestRazorSourceDocument.Create();
|
||||
var options = RazorParserOptions.CreateDefault();
|
||||
var context = new ParserContext(source, options);
|
||||
|
||||
var parser = new CSharpCodeParser(context);
|
||||
var diagnostics = new List<RazorDiagnostic>();
|
||||
|
||||
// Act
|
||||
parser.ValidateTagHelperPrefix(directiveText, directiveLocation, diagnostics);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDiagnostics, diagnostics);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("foo,assemblyName", 4)]
|
||||
[InlineData("foo, assemblyName", 5)]
|
||||
[InlineData(" foo, assemblyName", 8)]
|
||||
[InlineData(" foo , assemblyName", 11)]
|
||||
[InlineData("foo, assemblyName", 8)]
|
||||
[InlineData(" foo , assemblyName ", 14)]
|
||||
public void ParseAddOrRemoveDirective_CalculatesAssemblyLocationInLookupText(string text, int assemblyLocation)
|
||||
{
|
||||
// Arrange
|
||||
var source = TestRazorSourceDocument.Create();
|
||||
var options = RazorParserOptions.CreateDefault();
|
||||
var context = new ParserContext(source, options);
|
||||
|
||||
var parser = new CSharpCodeParser(context);
|
||||
|
||||
var directive = new CSharpCodeParser.ParsedDirective()
|
||||
{
|
||||
DirectiveText = text,
|
||||
};
|
||||
|
||||
var diagnostics = new List<RazorDiagnostic>();
|
||||
var expected = new SourceLocation(assemblyLocation, 0, assemblyLocation);
|
||||
|
||||
// Act
|
||||
var result = parser.ParseAddOrRemoveDirective(directive, SourceLocation.Zero, diagnostics);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(diagnostics);
|
||||
Assert.Equal("foo", result.TypePattern);
|
||||
Assert.Equal("assemblyName", result.AssemblyName);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", 1)]
|
||||
[InlineData("*,", 2)]
|
||||
[InlineData("?,", 2)]
|
||||
[InlineData(",", 1)]
|
||||
[InlineData(",,,", 3)]
|
||||
[InlineData("First, ", 7)]
|
||||
[InlineData("First , ", 8)]
|
||||
[InlineData(" ,Second", 8)]
|
||||
[InlineData(" , Second", 9)]
|
||||
[InlineData("SomeType,", 9)]
|
||||
[InlineData("SomeAssembly", 12)]
|
||||
[InlineData("First,Second,Third", 18)]
|
||||
public void ParseAddOrRemoveDirective_CreatesErrorIfInvalidLookupText_DoesNotThrow(string directiveText, int errorLength)
|
||||
{
|
||||
// Arrange
|
||||
var source = TestRazorSourceDocument.Create();
|
||||
var options = RazorParserOptions.CreateDefault();
|
||||
var context = new ParserContext(source, options);
|
||||
|
||||
var parser = new CSharpCodeParser(context);
|
||||
|
||||
var directive = new CSharpCodeParser.ParsedDirective()
|
||||
{
|
||||
DirectiveText = directiveText
|
||||
};
|
||||
|
||||
var diagnostics = new List<RazorDiagnostic>();
|
||||
var expectedError = RazorDiagnosticFactory.CreateParsing_InvalidTagHelperLookupText(
|
||||
new SourceSpan(new SourceLocation(1, 2, 3), errorLength), directiveText);
|
||||
|
||||
// Act
|
||||
var result = parser.ParseAddOrRemoveDirective(directive, new SourceLocation(1, 2, 3), diagnostics);
|
||||
|
||||
// Assert
|
||||
Assert.Same(directive, result);
|
||||
|
||||
var error = Assert.Single(diagnostics);
|
||||
Assert.Equal(expectedError, error);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TagHelperPrefixDirective_DuplicatesCauseError()
|
||||
{
|
||||
// Arrange
|
||||
var expectedDiagnostic = RazorDiagnosticFactory.CreateParsing_DuplicateDirective(
|
||||
new SourceSpan(null, 22 + Environment.NewLine.Length, 1, 0, 16), "tagHelperPrefix");
|
||||
var source = TestRazorSourceDocument.Create(
|
||||
@"@tagHelperPrefix ""th:""
|
||||
@tagHelperPrefix ""th""",
|
||||
filePath: null);
|
||||
|
||||
// Act
|
||||
var document = RazorSyntaxTree.Parse(source);
|
||||
|
||||
// Assert
|
||||
var erroredNode = document.Root.DescendantNodes().Last(n => n.GetSpanContext()?.ChunkGenerator is TagHelperPrefixDirectiveChunkGenerator);
|
||||
var chunkGenerator = Assert.IsType<TagHelperPrefixDirectiveChunkGenerator>(erroredNode.GetSpanContext().ChunkGenerator);
|
||||
var diagnostic = Assert.Single(chunkGenerator.Diagnostics);
|
||||
Assert.Equal(expectedDiagnostic, diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,345 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpErrorTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void HandlesQuotesAfterTransition()
|
||||
{
|
||||
ParseDocumentTest("@\"");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithHelperDirectiveProducesError()
|
||||
{
|
||||
ParseDocumentTest("@helper fooHelper { }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithNestedCodeBlockProducesError()
|
||||
{
|
||||
ParseDocumentTest("@if { @{} }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CapturesWhitespaceToEOLInInvalidUsingStmtAndTreatsAsFileCode()
|
||||
{
|
||||
// ParseBlockCapturesWhitespaceToEndOfLineInInvalidUsingStatementAndTreatsAsFileCode
|
||||
ParseDocumentTest(
|
||||
"@using " + Environment.NewLine + Environment.NewLine);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MethodOutputsOpenCurlyAsCodeSpanIfEofFoundAfterOpenCurlyBrace()
|
||||
{
|
||||
ParseDocumentTest("@{");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MethodOutputsZeroLengthCodeSpanIfStatementBlockEmpty()
|
||||
{
|
||||
ParseDocumentTest("@{}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MethodProducesErrorIfNewlineFollowsTransition()
|
||||
{
|
||||
ParseDocumentTest("@" + Environment.NewLine);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MethodProducesErrorIfWhitespaceBetweenTransitionAndBlockStartInEmbeddedExpr()
|
||||
{
|
||||
// ParseBlockMethodProducesErrorIfWhitespaceBetweenTransitionAndBlockStartInEmbeddedExpression
|
||||
ParseDocumentTest("@{" + Environment.NewLine
|
||||
+ " @ {}" + Environment.NewLine
|
||||
+ "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MethodProducesErrorIfEOFAfterTransitionInEmbeddedExpression()
|
||||
{
|
||||
ParseDocumentTest("@{" + Environment.NewLine
|
||||
+ " @");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MethodParsesNothingIfFirstCharacterIsNotIdentifierStartOrParenOrBrace()
|
||||
{
|
||||
ParseDocumentTest("@!!!");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReportErrorAndTerminateAtEOFIfIfParenInExplicitExprUnclosed()
|
||||
{
|
||||
// ParseBlockShouldReportErrorAndTerminateAtEOFIfIfParenInExplicitExpressionUnclosed
|
||||
ParseDocumentTest("@(foo bar" + Environment.NewLine
|
||||
+ "baz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReportErrorAndTerminateAtMarkupIfIfParenInExplicitExprUnclosed()
|
||||
{
|
||||
// ParseBlockShouldReportErrorAndTerminateAtMarkupIfIfParenInExplicitExpressionUnclosed
|
||||
ParseDocumentTest("@(foo bar" + Environment.NewLine
|
||||
+ "<html>" + Environment.NewLine
|
||||
+ "baz" + Environment.NewLine
|
||||
+ "</html");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyHandlesInCorrectTransitionsIfImplicitExpressionParensUnclosed()
|
||||
{
|
||||
ParseDocumentTest("@Href(" + Environment.NewLine
|
||||
+ "<h1>@Html.Foo(Bar);</h1>" + Environment.NewLine);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
// Test for fix to Dev10 884975 - Incorrect Error Messaging
|
||||
public void ShouldReportErrorAndTerminateAtEOFIfParenInImplicitExprUnclosed()
|
||||
{
|
||||
// ParseBlockShouldReportErrorAndTerminateAtEOFIfParenInImplicitExpressionUnclosed
|
||||
ParseDocumentTest("@Foo(Bar(Baz)" + Environment.NewLine
|
||||
+ "Biz" + Environment.NewLine
|
||||
+ "Boz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
// Test for fix to Dev10 884975 - Incorrect Error Messaging
|
||||
public void ShouldReportErrorAndTerminateAtMarkupIfParenInImplicitExpressionUnclosed()
|
||||
{
|
||||
// ParseBlockShouldReportErrorAndTerminateAtMarkupIfParenInImplicitExpressionUnclosed
|
||||
ParseDocumentTest("@Foo(Bar(Baz)" + Environment.NewLine
|
||||
+ "Biz" + Environment.NewLine
|
||||
+ "<html>" + Environment.NewLine
|
||||
+ "Boz" + Environment.NewLine
|
||||
+ "</html>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
// Test for fix to Dev10 884975 - Incorrect Error Messaging
|
||||
public void ShouldReportErrorAndTerminateAtEOFIfBracketInImplicitExpressionUnclosed()
|
||||
{
|
||||
// ParseBlockShouldReportErrorAndTerminateAtEOFIfBracketInImplicitExpressionUnclosed
|
||||
ParseDocumentTest("@Foo[Bar[Baz]" + Environment.NewLine
|
||||
+ "Biz" + Environment.NewLine
|
||||
+ "Boz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
// Test for fix to Dev10 884975 - Incorrect Error Messaging
|
||||
public void ShouldReportErrorAndTerminateAtMarkupIfBracketInImplicitExprUnclosed()
|
||||
{
|
||||
// ParseBlockShouldReportErrorAndTerminateAtMarkupIfBracketInImplicitExpressionUnclosed
|
||||
ParseDocumentTest("@Foo[Bar[Baz]" + Environment.NewLine
|
||||
+ "Biz" + Environment.NewLine
|
||||
+ "<b>" + Environment.NewLine
|
||||
+ "Boz" + Environment.NewLine
|
||||
+ "</b>");
|
||||
}
|
||||
|
||||
// Simple EOF handling errors:
|
||||
[Fact]
|
||||
public void ReportsErrorIfExplicitCodeBlockUnterminatedAtEOF()
|
||||
{
|
||||
ParseDocumentTest("@{ var foo = bar; if(foo != null) { bar(); } ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfClassBlockUnterminatedAtEOF()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@functions { var foo = bar; if(foo != null) { bar(); } ",
|
||||
new[] { FunctionsDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfIfBlockUnterminatedAtEOF()
|
||||
{
|
||||
RunUnterminatedSimpleKeywordBlock("@if");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfElseBlockUnterminatedAtEOF()
|
||||
{
|
||||
ParseDocumentTest("@if(foo) { baz(); } else { var foo = bar; if(foo != null) { bar(); } ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfElseIfBlockUnterminatedAtEOF()
|
||||
{
|
||||
ParseDocumentTest("@if(foo) { baz(); } else if { var foo = bar; if(foo != null) { bar(); } ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfDoBlockUnterminatedAtEOF()
|
||||
{
|
||||
ParseDocumentTest("@do { var foo = bar; if(foo != null) { bar(); } ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfTryBlockUnterminatedAtEOF()
|
||||
{
|
||||
ParseDocumentTest("@try { var foo = bar; if(foo != null) { bar(); } ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfCatchBlockUnterminatedAtEOF()
|
||||
{
|
||||
ParseDocumentTest("@try { baz(); } catch(Foo) { var foo = bar; if(foo != null) { bar(); } ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfFinallyBlockUnterminatedAtEOF()
|
||||
{
|
||||
ParseDocumentTest("@try { baz(); } finally { var foo = bar; if(foo != null) { bar(); } ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfForBlockUnterminatedAtEOF()
|
||||
{
|
||||
RunUnterminatedSimpleKeywordBlock("@for");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfForeachBlockUnterminatedAtEOF()
|
||||
{
|
||||
RunUnterminatedSimpleKeywordBlock("@foreach");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfWhileBlockUnterminatedAtEOF()
|
||||
{
|
||||
RunUnterminatedSimpleKeywordBlock("@while");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfSwitchBlockUnterminatedAtEOF()
|
||||
{
|
||||
RunUnterminatedSimpleKeywordBlock("@switch");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfLockBlockUnterminatedAtEOF()
|
||||
{
|
||||
RunUnterminatedSimpleKeywordBlock("@lock");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorIfUsingBlockUnterminatedAtEOF()
|
||||
{
|
||||
RunUnterminatedSimpleKeywordBlock("@using");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RequiresControlFlowStatementsToHaveBraces()
|
||||
{
|
||||
ParseDocumentTest("@if(foo) <p>Bar</p> else if(bar) <p>Baz</p> else <p>Boz</p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IncludesUnexpectedCharacterInSingleStatementControlFlowStatementError()
|
||||
{
|
||||
ParseDocumentTest("@if(foo)) { var bar = foo; }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OutputsErrorIfAtSignFollowedByLessThanSignAtStatementStart()
|
||||
{
|
||||
ParseDocumentTest("@if(foo) { @<p>Bar</p> }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesIfBlockAtEOLWhenRecoveringFromMissingCloseParen()
|
||||
{
|
||||
ParseDocumentTest("@if(foo bar" + Environment.NewLine
|
||||
+ "baz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesForeachBlockAtEOLWhenRecoveringFromMissingCloseParen()
|
||||
{
|
||||
ParseDocumentTest("@foreach(foo bar" + Environment.NewLine
|
||||
+ "baz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesWhileClauseInDoStmtAtEOLWhenRecoveringFromMissingCloseParen()
|
||||
{
|
||||
ParseDocumentTest("@do { } while(foo bar" + Environment.NewLine
|
||||
+ "baz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesUsingBlockAtEOLWhenRecoveringFromMissingCloseParen()
|
||||
{
|
||||
ParseDocumentTest("@using(foo bar" + Environment.NewLine
|
||||
+ "baz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResumesIfStatementAfterOpenParen()
|
||||
{
|
||||
ParseDocumentTest("@if(" + Environment.NewLine
|
||||
+ "else { <p>Foo</p> }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesNormalCSharpStringsAtEOLIfEndQuoteMissing()
|
||||
{
|
||||
ParseDocumentTest("@if(foo) {" + Environment.NewLine
|
||||
+ " var p = \"foo bar baz" + Environment.NewLine
|
||||
+ ";" + Environment.NewLine
|
||||
+ "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesNormalStringAtEndOfFile()
|
||||
{
|
||||
ParseDocumentTest("@if(foo) { var foo = \"blah blah blah blah blah");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesVerbatimStringAtEndOfFile()
|
||||
{
|
||||
ParseDocumentTest("@if(foo) { var foo = @\"blah " + Environment.NewLine
|
||||
+ "blah; " + Environment.NewLine
|
||||
+ "<p>Foo</p>" + Environment.NewLine
|
||||
+ "blah " + Environment.NewLine
|
||||
+ "blah");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyParsesMarkupIncorrectyAssumedToBeWithinAStatement()
|
||||
{
|
||||
ParseDocumentTest("@if(foo) {" + Environment.NewLine
|
||||
+ " var foo = \"foo bar baz" + Environment.NewLine
|
||||
+ " <p>Foo is @foo</p>" + Environment.NewLine
|
||||
+ "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyParsesAtSignInDelimitedBlock()
|
||||
{
|
||||
ParseDocumentTest("@(Request[\"description\"] ?? @photo.Description)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyRecoversFromMissingCloseParenInExpressionWithinCode()
|
||||
{
|
||||
ParseDocumentTest("@{string.Format(<html></html>}");
|
||||
}
|
||||
|
||||
private void RunUnterminatedSimpleKeywordBlock(string keyword)
|
||||
{
|
||||
ParseDocumentTest(
|
||||
keyword + " (foo) { var foo = bar; if(foo != null) { bar(); } ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpExplicitExpressionTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void ShouldOutputZeroLengthCodeSpanIfExplicitExpressionIsEmpty()
|
||||
{
|
||||
ParseDocumentTest("@()");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldOutputZeroLengthCodeSpanIfEOFOccursAfterStartOfExplicitExpr()
|
||||
{
|
||||
// ParseBlockShouldOutputZeroLengthCodeSpanIfEOFOccursAfterStartOfExplicitExpression
|
||||
ParseDocumentTest("@(");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAcceptEscapedQuoteInNonVerbatimStrings()
|
||||
{
|
||||
ParseDocumentTest("@(\"\\\"\")");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAcceptEscapedQuoteInVerbatimStrings()
|
||||
{
|
||||
ParseDocumentTest("@(@\"\"\"\")");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAcceptMultipleRepeatedEscapedQuoteInVerbatimStrings()
|
||||
{
|
||||
ParseDocumentTest("@(@\"\"\"\"\"\")");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAcceptMultiLineVerbatimStrings()
|
||||
{
|
||||
ParseDocumentTest(@"@(@""" + Environment.NewLine
|
||||
+ @"Foo" + Environment.NewLine
|
||||
+ @"Bar" + Environment.NewLine
|
||||
+ @"Baz" + Environment.NewLine
|
||||
+ @""")");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAcceptMultipleEscapedQuotesInNonVerbatimStrings()
|
||||
{
|
||||
ParseDocumentTest("@(\"\\\"hello, world\\\"\")");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAcceptMultipleEscapedQuotesInVerbatimStrings()
|
||||
{
|
||||
ParseDocumentTest("@(@\"\"\"hello, world\"\"\")");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAcceptConsecutiveEscapedQuotesInNonVerbatimStrings()
|
||||
{
|
||||
ParseDocumentTest("@(\"\\\"\\\"\")");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAcceptConsecutiveEscapedQuotesInVerbatimStrings()
|
||||
{
|
||||
ParseDocumentTest("@(@\"\"\"\"\"\")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpFunctionsTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void MarkupInFunctionsBlock_DoesNotParseWhenNotSupported()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
RazorLanguageVersion.Version_2_1,
|
||||
@"
|
||||
@functions {
|
||||
void Announcment(string message)
|
||||
{
|
||||
<h3>@message</h3>
|
||||
}
|
||||
}
|
||||
", new[] { FunctionsDirective.Directive, }, designTime: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MarkupInFunctionsBlock_ParsesMarkupInsideMethod()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
RazorLanguageVersion.Version_3_0,
|
||||
@"
|
||||
@functions {
|
||||
void Announcment(string message)
|
||||
{
|
||||
<h3>@message</h3>
|
||||
}
|
||||
}
|
||||
", new[] { FunctionsDirective.Directive, }, designTime: false);
|
||||
}
|
||||
|
||||
// This will parse correctly in Razor, but will generate invalid C#.
|
||||
[Fact]
|
||||
public void MarkupInFunctionsBlock_ParsesMarkupWithExpressionsMethod()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
RazorLanguageVersion.Version_3_0,
|
||||
@"
|
||||
@functions {
|
||||
void Announcment(string message) => <h3>@message</h3>
|
||||
}
|
||||
", new[] { FunctionsDirective.Directive, }, designTime: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MarkupInFunctionsBlock_DoesNotParseMarkupInString()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
RazorLanguageVersion.Version_3_0,
|
||||
@"
|
||||
@functions {
|
||||
void Announcment(string message) => ""<h3>@message</h3>"";
|
||||
}
|
||||
", new[] { FunctionsDirective.Directive, }, designTime: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MarkupInFunctionsBlock_DoesNotParseMarkupInVerbatimString()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
RazorLanguageVersion.Version_3_0,
|
||||
@"
|
||||
@functions {
|
||||
void Announcment(string message) => @""<h3>@message</h3>"";
|
||||
}
|
||||
", new[] { FunctionsDirective.Directive, }, designTime: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MarkupInFunctionsBlock_CanContainCurlyBraces()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
RazorLanguageVersion.Version_3_0,
|
||||
@"
|
||||
@functions {
|
||||
void Announcment(string message)
|
||||
{
|
||||
<div>
|
||||
@if (message.Length > 0)
|
||||
{
|
||||
<p>@message.Length</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
", new[] { FunctionsDirective.Directive, }, designTime: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MarkupInFunctionsBlock_MarkupCanContainTemplate()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
RazorLanguageVersion.Version_3_0,
|
||||
@"
|
||||
@functions {
|
||||
void Announcment(string message)
|
||||
{
|
||||
<div>
|
||||
@if (message.Length > 0)
|
||||
{
|
||||
Repeat(@<p>@message.Length</p>);
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
", new[] { FunctionsDirective.Directive, }, designTime: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReservedKeywordsInFunctionsBlock_WithMarkup()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
RazorLanguageVersion.Version_3_0,
|
||||
@"
|
||||
@functions {
|
||||
class Person
|
||||
{
|
||||
public void DoSomething()
|
||||
{
|
||||
<p>Just do it!</p>
|
||||
}
|
||||
}
|
||||
}
|
||||
", new[] { FunctionsDirective.Directive, }, designTime: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpImplicitExpressionTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket1()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val??[");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket2()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val??[0");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket3()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket4()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?(");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket5()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[more");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket6()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[0]");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket7()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[<p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket8()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[more.<p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket9()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val??[more<p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket10()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[-1]?");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket11()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[abc]?[def");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket12()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[abc]?[2]");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket13()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[abc]?.more?[def]");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket14()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[abc]?.more?.abc");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket15()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[null ?? true]");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Bracket16()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?[abc?.gef?[-1]]");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot1()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot2()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val??");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot3()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val??more");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot4()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?!");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot5()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot6()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val??.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot7()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?.(abc)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot8()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?.<p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot9()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?.more");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot10()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?.more<p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot11()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val??.more<p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot12()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?.more(false)?.<p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot13()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?.more(false)?.abc");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNullConditionalOperatorImplicitExpression_Dot14()
|
||||
{
|
||||
// Act & Assert
|
||||
ParseDocumentTest("@val?.more(null ?? true)?.abc");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedImplicitExpression()
|
||||
{
|
||||
ParseDocumentTest("if (true) { @foo }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AcceptsNonEnglishCharactersThatAreValidIdentifiers()
|
||||
{
|
||||
ParseDocumentTest("@हळूँजद॔.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OutputsZeroLengthCodeSpanIfInvalidCharacterFollowsTransition()
|
||||
{
|
||||
ParseDocumentTest("@/");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OutputsZeroLengthCodeSpanIfEOFOccursAfterTransition()
|
||||
{
|
||||
ParseDocumentTest("@");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsSlashesWithinComplexImplicitExpressions()
|
||||
{
|
||||
ParseDocumentTest("@DataGridColumn.Template(\"Years of Service\", e => (int)Math.Round((DateTime.Now - dt).TotalDays / 365))");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesSingleIdentifierAsImplicitExpression()
|
||||
{
|
||||
ParseDocumentTest("@foo");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotAcceptSemicolonIfExpressionTerminatedByWhitespace()
|
||||
{
|
||||
ParseDocumentTest("@foo ;");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IgnoresSemicolonAtEndOfSimpleImplicitExpression()
|
||||
{
|
||||
ParseDocumentTest("@foo;");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesDottedIdentifiersAsImplicitExpression()
|
||||
{
|
||||
ParseDocumentTest("@foo.bar.baz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IgnoresSemicolonAtEndOfDottedIdentifiers()
|
||||
{
|
||||
ParseDocumentTest("@foo.bar.baz;");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotIncludeDotAtEOFInImplicitExpression()
|
||||
{
|
||||
ParseDocumentTest("@foo.bar.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotIncludeDotFollowedByInvalidIdentifierCharInImplicitExpr1()
|
||||
{
|
||||
// ParseBlockMethodDoesNotIncludeDotFollowedByInvalidIdentifierCharacterInImplicitExpression1
|
||||
ParseDocumentTest("@foo.bar.0");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotIncludeDotFollowedByInvalidIdentifierCharInImplicitExpr2()
|
||||
{
|
||||
// ParseBlockMethodDoesNotIncludeDotFollowedByInvalidIdentifierCharacterInImplicitExpression2
|
||||
ParseDocumentTest("@foo.bar.</p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotIncludeSemicolonAfterDot()
|
||||
{
|
||||
ParseDocumentTest("@foo.bar.;");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesAfterIdentifierUnlessFollowedByDotOrParenInImplicitExpr()
|
||||
{
|
||||
// ParseBlockMethodTerminatesAfterIdentifierUnlessFollowedByDotOrParenInImplicitExpression
|
||||
ParseDocumentTest("@foo.bar</p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProperlyParsesParenthesesAndBalancesThemInImplicitExpression()
|
||||
{
|
||||
ParseDocumentTest(@"@foo().bar(""bi\""z"", 4)(""chained method; call"").baz(@""bo""""z"", '\'', () => { return 4; }, (4+5+new { foo = bar[4] }))");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProperlyParsesBracketsAndBalancesThemInImplicitExpression()
|
||||
{
|
||||
ParseDocumentTest(@"@foo.bar[4 * (8 + 7)][""fo\""o""].baz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesImplicitExpressionAtHtmlEndTag()
|
||||
{
|
||||
ParseDocumentTest("@foo().bar.baz</p>zoop");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesImplicitExpressionAtHtmlStartTag()
|
||||
{
|
||||
ParseDocumentTest("@foo().bar.baz<p>zoop");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesImplicitExprBeforeDotIfDotNotFollowedByIdentifierStartChar()
|
||||
{
|
||||
// ParseBlockTerminatesImplicitExpressionBeforeDotIfDotNotFollowedByIdentifierStartCharacter
|
||||
ParseDocumentTest("@foo().bar.baz.42");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StopsBalancingParenthesesAtEOF()
|
||||
{
|
||||
ParseDocumentTest("@foo(()");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesImplicitExpressionIfCloseParenFollowedByAnyWhiteSpace()
|
||||
{
|
||||
ParseDocumentTest("@foo.bar() (baz)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesImplicitExpressionIfIdentifierFollowedByAnyWhiteSpace()
|
||||
{
|
||||
ParseDocumentTest("@foo .bar() (baz)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminatesImplicitExpressionAtLastValidPointIfDotFollowedByWhitespace()
|
||||
{
|
||||
ParseDocumentTest("@foo. bar() (baz)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OutputExpressionIfModuleTokenNotFollowedByBrace()
|
||||
{
|
||||
ParseDocumentTest("@module.foo()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpLanguageCharacteristicsTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetSample_RightShiftAssign_ReturnsCorrectToken()
|
||||
{
|
||||
// Arrange & Act
|
||||
var token = CSharpLanguageCharacteristics.Instance.GetSample(SyntaxKind.RightShiftAssign);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(">>=", token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpNestedStatementsTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void NestedSimpleStatement()
|
||||
{
|
||||
ParseDocumentTest("@while(true) { foo(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedKeywordStatement()
|
||||
{
|
||||
ParseDocumentTest("@while(true) { for(int i = 0; i < 10; i++) { foo(); } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedCodeBlock()
|
||||
{
|
||||
ParseDocumentTest("@while(true) { { { { foo(); } } } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedImplicitExpression()
|
||||
{
|
||||
ParseDocumentTest("@while(true) { @foo }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedExplicitExpression()
|
||||
{
|
||||
ParseDocumentTest("@while(true) { @(foo) }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedMarkupBlock()
|
||||
{
|
||||
ParseDocumentTest("@while(true) { <p>Hello</p> }");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpRazorCommentsTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void UnterminatedRazorComment()
|
||||
{
|
||||
ParseDocumentTest("@*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyRazorComment()
|
||||
{
|
||||
ParseDocumentTest("@**@");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RazorCommentInImplicitExpressionMethodCall()
|
||||
{
|
||||
ParseDocumentTest("@foo(" + Environment.NewLine
|
||||
+ "@**@" + Environment.NewLine);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnterminatedRazorCommentInImplicitExpressionMethodCall()
|
||||
{
|
||||
ParseDocumentTest("@foo(@*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RazorMultilineCommentInBlock()
|
||||
{
|
||||
ParseDocumentTest(@"
|
||||
@{
|
||||
@*
|
||||
This is a comment
|
||||
*@
|
||||
}
|
||||
");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RazorCommentInVerbatimBlock()
|
||||
{
|
||||
ParseDocumentTest("@{" + Environment.NewLine
|
||||
+ " <text" + Environment.NewLine
|
||||
+ " @**@" + Environment.NewLine
|
||||
+ "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RazorCommentInOpeningTagBlock()
|
||||
{
|
||||
ParseDocumentTest("<text @* razor comment *@></text>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RazorCommentInClosingTagBlock()
|
||||
{
|
||||
ParseDocumentTest("<text></text @* razor comment *@>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnterminatedRazorCommentInVerbatimBlock()
|
||||
{
|
||||
ParseDocumentTest("@{@*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RazorCommentInMarkup()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"<p>" + Environment.NewLine
|
||||
+ "@**@" + Environment.NewLine
|
||||
+ "</p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleRazorCommentInMarkup()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"<p>" + Environment.NewLine
|
||||
+ " @**@ " + Environment.NewLine
|
||||
+ "@**@" + Environment.NewLine
|
||||
+ "</p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleRazorCommentsInSameLineInMarkup()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"<p>" + Environment.NewLine
|
||||
+ "@**@ @**@" + Environment.NewLine
|
||||
+ "</p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RazorCommentsSurroundingMarkup()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"<p>" + Environment.NewLine
|
||||
+ "@* hello *@ content @* world *@" + Environment.NewLine
|
||||
+ "</p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RazorCommentBetweenCodeBlockAndMarkup()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@{ }" + Environment.NewLine +
|
||||
"@* Hello World *@" + Environment.NewLine +
|
||||
"<div>Foo</div>"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RazorCommentWithExtraNewLineInMarkup()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"<p>" + Environment.NewLine + Environment.NewLine
|
||||
+ "@* content *@" + Environment.NewLine
|
||||
+ "@*" + Environment.NewLine
|
||||
+ "content" + Environment.NewLine
|
||||
+ "*@" + Environment.NewLine + Environment.NewLine
|
||||
+ "</p>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpReservedWordsTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void ReservedWord()
|
||||
{
|
||||
ParseDocumentTest("@namespace");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private void ReservedWordIsCaseSensitive()
|
||||
{
|
||||
ParseDocumentTest("@NameSpace");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpSectionTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void CapturesNewlineImmediatelyFollowing()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section" + Environment.NewLine,
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CapturesWhitespaceToEndOfLineInSectionStatementMissingOpenBrace()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section Foo " + Environment.NewLine + " ",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CapturesWhitespaceToEndOfLineInSectionStatementMissingName()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section " + Environment.NewLine + " ",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IgnoresSectionUnlessAllLowerCase()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@Section foo",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorAndTerminatesSectionBlockIfKeywordNotFollowedByIdentifierStartChar()
|
||||
{
|
||||
// ParseSectionBlockReportsErrorAndTerminatesSectionBlockIfKeywordNotFollowedByIdentifierStartCharacter
|
||||
ParseDocumentTest(
|
||||
"@section 9 { <p>Foo</p> }",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorAndTerminatesSectionBlockIfNameNotFollowedByOpenBrace()
|
||||
{
|
||||
// ParseSectionBlockReportsErrorAndTerminatesSectionBlockIfNameNotFollowedByOpenBrace
|
||||
ParseDocumentTest(
|
||||
"@section foo-bar { <p>Foo</p> }",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParserOutputsErrorOnNestedSections()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section foo { @section bar { <p>Foo</p> } }",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParserOutputsErrorOnMultipleNestedSections()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section foo { @section bar { <p>Foo</p> @section baz { } } }",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParserDoesNotOutputErrorOtherNestedDirectives()
|
||||
{
|
||||
// This isn't a real scenario but we just want to verify we don't show misleading errors.
|
||||
ParseDocumentTest(
|
||||
"@section foo { @inherits Bar }",
|
||||
new[] { SectionDirective.Directive, InheritsDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesEOFAfterOpenBrace()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section foo {",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesEOFAfterOpenContent1()
|
||||
{
|
||||
|
||||
ParseDocumentTest(
|
||||
"@section foo { ",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesEOFAfterOpenContent2()
|
||||
{
|
||||
|
||||
ParseDocumentTest(
|
||||
"@section foo {\n",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesEOFAfterOpenContent3()
|
||||
{
|
||||
|
||||
ParseDocumentTest(
|
||||
"@section foo {abc",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesEOFAfterOpenContent4()
|
||||
{
|
||||
|
||||
ParseDocumentTest(
|
||||
"@section foo {\n abc",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesUnterminatedSection()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section foo { <p>Foo{}</p>",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesUnterminatedSectionWithNestedIf()
|
||||
{
|
||||
// Arrange
|
||||
var newLine = Environment.NewLine;
|
||||
var spaces = " ";
|
||||
|
||||
// Act & Assert
|
||||
ParseDocumentTest(
|
||||
string.Format(
|
||||
"@section Test{0}{{{0}{1}@if(true){0}{1}{{{0}{1}{1}<p>Hello World</p>{0}{1}}}",
|
||||
newLine,
|
||||
spaces),
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReportsErrorAndAcceptsWhitespaceToEOLIfSectionNotFollowedByOpenBrace()
|
||||
{
|
||||
// ParseSectionBlockReportsErrorAndAcceptsWhitespaceToEndOfLineIfSectionNotFollowedByOpenBrace
|
||||
ParseDocumentTest(
|
||||
"@section foo " + Environment.NewLine,
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AcceptsOpenBraceMultipleLinesBelowSectionName()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section foo "
|
||||
+ Environment.NewLine
|
||||
+ Environment.NewLine
|
||||
+ Environment.NewLine
|
||||
+ Environment.NewLine
|
||||
+ Environment.NewLine
|
||||
+ Environment.NewLine
|
||||
+ "{" + Environment.NewLine
|
||||
+ "<p>Foo</p>" + Environment.NewLine
|
||||
+ "}",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesNamedSectionCorrectly()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section foo { <p>Foo</p> }",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoesNotRequireSpaceBetweenSectionNameAndOpenBrace()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section foo{ <p>Foo</p> }",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BalancesBraces()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section foo { <script>(function foo() { return 1; })();</script> }",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllowsBracesInCSharpExpression()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section foo { I really want to render a close brace, so here I go: @(\"}\") }",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SectionIsCorrectlyTerminatedWhenCloseBraceImmediatelyFollowsCodeBlock()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section Foo {" + Environment.NewLine
|
||||
+ "@if(true) {" + Environment.NewLine
|
||||
+ "}" + Environment.NewLine
|
||||
+ "}",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SectionCorrectlyTerminatedWhenCloseBraceFollowsCodeBlockNoWhitespace()
|
||||
{
|
||||
// SectionIsCorrectlyTerminatedWhenCloseBraceImmediatelyFollowsCodeBlockNoWhitespace
|
||||
ParseDocumentTest(
|
||||
"@section Foo {" + Environment.NewLine
|
||||
+ "@if(true) {" + Environment.NewLine
|
||||
+ "}}",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyTerminatesWhenCloseBraceImmediatelyFollowsMarkup()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section foo {something}",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesComment()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section s {<!-- -->}",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
// This was a user reported bug (codeplex #710), the section parser wasn't handling
|
||||
// comments.
|
||||
[Fact]
|
||||
public void ParsesCommentWithDelimiters()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section s {<!-- > \" '-->}",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CommentRecoversFromUnclosedTag()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section s {" + Environment.NewLine + "<a" + Environment.NewLine + "<!-- > \" '-->}",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesXmlProcessingInstruction()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@section s { <? xml bleh ?>}",
|
||||
new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void _WithDoubleTransition1()
|
||||
{
|
||||
ParseDocumentTest("@section s {<span foo='@@' />}", new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void _WithDoubleTransition2()
|
||||
{
|
||||
ParseDocumentTest("@section s {<span foo='@DateTime.Now @@' />}", new[] { SectionDirective.Directive });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpSpecialBlockTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void NonKeywordStatementInCodeBlockIsHandledCorrectly()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
@"@{
|
||||
List<dynamic> photos = gallery.Photo.ToList();
|
||||
}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BalancesBracesOutsideStringsIfFirstCharIsBraceAndReturnsSpanOfTypeCode()
|
||||
{
|
||||
// ParseBlockBalancesBracesOutsideStringsIfFirstCharacterIsBraceAndReturnsSpanOfTypeCode
|
||||
ParseDocumentTest("@{foo\"b}ar\" if(condition) { string.Format(\"{0}\"); } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BalancesParensOutsideStringsIfFirstCharIsParenAndReturnsSpanOfTypeExpr()
|
||||
{
|
||||
// ParseBlockBalancesParensOutsideStringsIfFirstCharacterIsParenAndReturnsSpanOfTypeExpression
|
||||
ParseDocumentTest("@(foo\"b)ar\" if(condition) { string.Format(\"{0}\"); } )");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseBlockIgnoresSingleSlashAtStart()
|
||||
{
|
||||
ParseDocumentTest("@/ foo");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseBlockTerminatesSingleLineCommentAtEndOfLine()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
"@if(!false) {" + Environment.NewLine +
|
||||
" // Foo" + Environment.NewLine +
|
||||
"\t<p>A real tag!</p>" + Environment.NewLine +
|
||||
"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
// Basic Tests for C# Statements:
|
||||
// * Basic case for each statement
|
||||
// * Basic case for ALL clauses
|
||||
|
||||
// This class DOES NOT contain
|
||||
// * Error cases
|
||||
// * Tests for various types of nested statements
|
||||
// * Comment tests
|
||||
|
||||
public class CSharpStatementTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void ForStatement()
|
||||
{
|
||||
ParseDocumentTest("@for(int i = 0; i++; i < length) { foo(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForEachStatement()
|
||||
{
|
||||
ParseDocumentTest("@foreach(var foo in bar) { foo(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AwaitForEachStatement()
|
||||
{
|
||||
ParseDocumentTest("@await foreach(var foo in bar) { foo(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MalformedAwaitForEachStatement()
|
||||
{
|
||||
ParseDocumentTest("@await foreach(var foo in bar { foo(); ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhileStatement()
|
||||
{
|
||||
ParseDocumentTest("@while(true) { foo(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SwitchStatement()
|
||||
{
|
||||
ParseDocumentTest("@switch(foo) { foo(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LockStatement()
|
||||
{
|
||||
ParseDocumentTest("@lock(baz) { foo(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IfStatement()
|
||||
{
|
||||
ParseDocumentTest("@if(true) { foo(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ElseIfClause()
|
||||
{
|
||||
ParseDocumentTest("@if(true) { foo(); } else if(false) { foo(); } else if(!false) { foo(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ElseClause()
|
||||
{
|
||||
ParseDocumentTest("@if(true) { foo(); } else { foo(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryStatement()
|
||||
{
|
||||
ParseDocumentTest("@try { foo(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CatchClause()
|
||||
{
|
||||
ParseDocumentTest("@try { foo(); } catch(IOException ioex) { handleIO(); } catch(Exception ex) { handleOther(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionFilter_TryCatchWhenComplete_SingleLine()
|
||||
{
|
||||
ParseDocumentTest("@try { someMethod(); } catch(Exception) when (true) { handleIO(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionFilter_TryCatchWhenFinallyComplete_SingleLine()
|
||||
{
|
||||
ParseDocumentTest("@try { A(); } catch(Exception) when (true) { B(); } finally { C(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionFilter_TryCatchWhenCatchWhenComplete_SingleLine()
|
||||
{
|
||||
ParseDocumentTest("@try { A(); } catch(Exception) when (true) { B(); } catch(IOException) when (false) { C(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionFilter_MultiLine()
|
||||
{
|
||||
ParseDocumentTest(
|
||||
@"@try
|
||||
{
|
||||
A();
|
||||
}
|
||||
catch(Exception) when (true)
|
||||
{
|
||||
B();
|
||||
}
|
||||
catch(IOException) when (false)
|
||||
{
|
||||
C();
|
||||
}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionFilter_NestedTryCatchWhen()
|
||||
{
|
||||
ParseDocumentTest("@{try { someMethod(); } catch(Exception) when (true) { handleIO(); }}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionFilter_IncompleteTryCatchWhen()
|
||||
{
|
||||
ParseDocumentTest("@try { someMethod(); } catch(Exception) when");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionFilter_IncompleteTryWhen()
|
||||
{
|
||||
ParseDocumentTest("@try { someMethod(); } when");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionFilter_IncompleteTryCatchNoBodyWhen()
|
||||
{
|
||||
ParseDocumentTest("@try { someMethod(); } catch(Exception) when { anotherMethod(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionFilter_IncompleteTryCatchWhenNoBodies()
|
||||
{
|
||||
ParseDocumentTest("@try { someMethod(); } catch(Exception) when (true)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionFilterError_TryCatchWhen_InCompleteCondition()
|
||||
{
|
||||
ParseDocumentTest("@try { someMethod(); } catch(Exception) when (");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionFilterError_TryCatchWhen_InCompleteBody()
|
||||
{
|
||||
ParseDocumentTest("@try { someMethod(); } catch(Exception) when (true) {");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FinallyClause()
|
||||
{
|
||||
ParseDocumentTest("@try { foo(); } finally { Dispose(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Using_VariableDeclaration_Simple()
|
||||
{
|
||||
ParseDocumentTest("@{ using var foo = someDisposable; }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Using_VariableDeclaration_Complex()
|
||||
{
|
||||
ParseDocumentTest("@{ using Some.Disposable.TypeName foo = GetDisposable<Some.Disposable.TypeName>(() => { using var bar = otherDisposable; }); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StaticUsing_NoUsing()
|
||||
{
|
||||
ParseDocumentTest("@using static");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StaticUsing_SingleIdentifier()
|
||||
{
|
||||
ParseDocumentTest("@using static System");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StaticUsing_MultipleIdentifiers()
|
||||
{
|
||||
ParseDocumentTest("@using static System.Console");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StaticUsing_GlobalPrefix()
|
||||
{
|
||||
ParseDocumentTest("@using static global::System.Console");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StaticUsing_Complete_Spaced()
|
||||
{
|
||||
ParseDocumentTest("@using static global::System.Console ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UsingStatement()
|
||||
{
|
||||
ParseDocumentTest("@using(var foo = new Foo()) { foo.Bar(); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UsingTypeAlias()
|
||||
{
|
||||
ParseDocumentTest("@using StringDictionary = System.Collections.Generic.Dictionary<string, string>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UsingNamespaceImport()
|
||||
{
|
||||
ParseDocumentTest("@using System.Text.Encoding.ASCIIEncoding");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoStatement()
|
||||
{
|
||||
ParseDocumentTest("@do { foo(); } while(true);");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NonBlockKeywordTreatedAsImplicitExpression()
|
||||
{
|
||||
ParseDocumentTest("@is foo");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpTemplateTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void HandlesSingleLineTemplate()
|
||||
{
|
||||
ParseDocumentTest("@{ var foo = @: bar" + Environment.NewLine + "; }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesSingleLineImmediatelyFollowingStatementChar()
|
||||
{
|
||||
ParseDocumentTest("@{i@: bar" + Environment.NewLine + "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesSimpleTemplateInExplicitExpressionParens()
|
||||
{
|
||||
ParseDocumentTest("@(Html.Repeat(10, @<p>Foo #@item</p>))");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesSimpleTemplateInImplicitExpressionParens()
|
||||
{
|
||||
ParseDocumentTest("@Html.Repeat(10, @<p>Foo #@item</p>)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesTwoTemplatesInImplicitExpressionParens()
|
||||
{
|
||||
ParseDocumentTest("@Html.Repeat(10, @<p>Foo #@item</p>, @<p>Foo #@item</p>)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProducesErrorButCorrectlyParsesNestedTemplateInImplicitExprParens()
|
||||
{
|
||||
// ParseBlockProducesErrorButCorrectlyParsesNestedTemplateInImplicitExpressionParens
|
||||
ParseDocumentTest("@Html.Repeat(10, @<p>Foo #@Html.Repeat(10, @<p>@item</p>)</p>)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesSimpleTemplateInStatementWithinCodeBlock()
|
||||
{
|
||||
ParseDocumentTest("@foreach(foo in Bar) { Html.ExecuteTemplate(foo, @<p>Foo #@item</p>); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesTwoTemplatesInStatementWithinCodeBlock()
|
||||
{
|
||||
ParseDocumentTest("@foreach(foo in Bar) { Html.ExecuteTemplate(foo, @<p>Foo #@item</p>, @<p>Foo #@item</p>); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProducesErrorButCorrectlyParsesNestedTemplateInStmtWithinCodeBlock()
|
||||
{
|
||||
// ParseBlockProducesErrorButCorrectlyParsesNestedTemplateInStatementWithinCodeBlock
|
||||
ParseDocumentTest("@foreach(foo in Bar) { Html.ExecuteTemplate(foo, @<p>Foo #@Html.Repeat(10, @<p>@item</p>)</p>); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesSimpleTemplateInStatementWithinStatementBlock()
|
||||
{
|
||||
ParseDocumentTest("@{ var foo = bar; Html.ExecuteTemplate(foo, @<p>Foo #@item</p>); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlessTwoTemplatesInStatementWithinStatementBlock()
|
||||
{
|
||||
ParseDocumentTest("@{ var foo = bar; Html.ExecuteTemplate(foo, @<p>Foo #@item</p>, @<p>Foo #@item</p>); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProducesErrorButCorrectlyParsesNestedTemplateInStmtWithinStmtBlock()
|
||||
{
|
||||
// ParseBlockProducesErrorButCorrectlyParsesNestedTemplateInStatementWithinStatementBlock
|
||||
ParseDocumentTest("@{ var foo = bar; Html.ExecuteTemplate(foo, @<p>Foo #@Html.Repeat(10, @<p>@item</p>)</p>); }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void _WithDoubleTransition_DoesNotThrow()
|
||||
{
|
||||
ParseDocumentTest("@{ var foo = bar; Html.ExecuteTemplate(foo, @<p foo='@@'>Foo #@item</p>); }");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||
{
|
||||
public class CSharpToMarkupSwitchTest : ParserTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void SingleAngleBracketDoesNotCauseSwitchIfOuterBlockIsTerminated()
|
||||
{
|
||||
ParseDocumentTest("@{ List< }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GivesSpacesToCodeOnAtTagTemplateTransitionInDesignTimeMode()
|
||||
{
|
||||
ParseDocumentTest("@Foo( @<p>Foo</p> )", designTime: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GivesSpacesToCodeOnAtColonTemplateTransitionInDesignTimeMode()
|
||||
{
|
||||
ParseDocumentTest("@Foo( " + Environment.NewLine
|
||||
+ "@:<p>Foo</p> " + Environment.NewLine
|
||||
+ ")", designTime: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GivesSpacesToCodeOnTagTransitionInDesignTimeMode()
|
||||
{
|
||||
ParseDocumentTest("@{" + Environment.NewLine
|
||||
+ " <p>Foo</p> " + Environment.NewLine
|
||||
+ "}", designTime: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GivesSpacesToCodeOnInvalidAtTagTransitionInDesignTimeMode()
|
||||
{
|
||||
ParseDocumentTest("@{" + Environment.NewLine
|
||||
+ " @<p>Foo</p> " + Environment.NewLine
|
||||
+ "}", designTime: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GivesSpacesToCodeOnAtColonTransitionInDesignTimeMode()
|
||||
{
|
||||
ParseDocumentTest("@{" + Environment.NewLine
|
||||
+ " @:<p>Foo</p> " + Environment.NewLine
|
||||
+ "}", designTime: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldSupportSingleLineMarkupContainingStatementBlock()
|
||||
{
|
||||
ParseDocumentTest("@Repeat(10," + Environment.NewLine
|
||||
+ " @: @{}" + Environment.NewLine
|
||||
+ ")");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldSupportMarkupWithoutPreceedingWhitespace()
|
||||
{
|
||||
ParseDocumentTest("@foreach(var file in files){" + Environment.NewLine
|
||||
+ Environment.NewLine
|
||||
+ Environment.NewLine
|
||||
+ "@:Baz" + Environment.NewLine
|
||||
+ "<br/>" + Environment.NewLine
|
||||
+ "<a>Foo</a>" + Environment.NewLine
|
||||
+ "@:Bar" + Environment.NewLine
|
||||
+ "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GivesAllWhitespaceOnSameLineWithTrailingNewLineToMarkupExclPreceedingNewline()
|
||||
{
|
||||
// ParseBlockGivesAllWhitespaceOnSameLineExcludingPreceedingNewlineButIncludingTrailingNewLineToMarkup
|
||||
ParseDocumentTest("@if(foo) {" + Environment.NewLine
|
||||
+ " var foo = \"After this statement there are 10 spaces\"; " + Environment.NewLine
|
||||
+ " <p>" + Environment.NewLine
|
||||
+ " Foo" + Environment.NewLine
|
||||
+ " @bar" + Environment.NewLine
|
||||
+ " </p>" + Environment.NewLine
|
||||
+ " @:Hello!" + Environment.NewLine
|
||||
+ " var biz = boz;" + Environment.NewLine
|
||||
+ "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllowsMarkupInIfBodyWithBraces()
|
||||
{
|
||||
ParseDocumentTest("@if(foo) { <p>Bar</p> } else if(bar) { <p>Baz</p> } else { <p>Boz</p> }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllowsMarkupInIfBodyWithBracesWithinCodeBlock()
|
||||
{
|
||||
ParseDocumentTest("@{ if(foo) { <p>Bar</p> } else if(bar) { <p>Baz</p> } else { <p>Boz</p> } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsMarkupInCaseAndDefaultBranchesOfSwitch()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@switch(foo) {" + Environment.NewLine
|
||||
+ " case 0:" + Environment.NewLine
|
||||
+ " <p>Foo</p>" + Environment.NewLine
|
||||
+ " break;" + Environment.NewLine
|
||||
+ " case 1:" + Environment.NewLine
|
||||
+ " <p>Bar</p>" + Environment.NewLine
|
||||
+ " return;" + Environment.NewLine
|
||||
+ " case 2:" + Environment.NewLine
|
||||
+ " {" + Environment.NewLine
|
||||
+ " <p>Baz</p>" + Environment.NewLine
|
||||
+ " <p>Boz</p>" + Environment.NewLine
|
||||
+ " }" + Environment.NewLine
|
||||
+ " default:" + Environment.NewLine
|
||||
+ " <p>Biz</p>" + Environment.NewLine
|
||||
+ "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsMarkupInCaseAndDefaultBranchesOfSwitchInCodeBlock()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@{ switch(foo) {" + Environment.NewLine
|
||||
+ " case 0:" + Environment.NewLine
|
||||
+ " <p>Foo</p>" + Environment.NewLine
|
||||
+ " break;" + Environment.NewLine
|
||||
+ " case 1:" + Environment.NewLine
|
||||
+ " <p>Bar</p>" + Environment.NewLine
|
||||
+ " return;" + Environment.NewLine
|
||||
+ " case 2:" + Environment.NewLine
|
||||
+ " {" + Environment.NewLine
|
||||
+ " <p>Baz</p>" + Environment.NewLine
|
||||
+ " <p>Boz</p>" + Environment.NewLine
|
||||
+ " }" + Environment.NewLine
|
||||
+ " default:" + Environment.NewLine
|
||||
+ " <p>Biz</p>" + Environment.NewLine
|
||||
+ "} }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesMarkupStatementOnOpenAngleBracket()
|
||||
{
|
||||
ParseDocumentTest("@for(int i = 0; i < 10; i++) { <p>Foo</p> }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesMarkupStatementOnOpenAngleBracketInCodeBlock()
|
||||
{
|
||||
ParseDocumentTest("@{ for(int i = 0; i < 10; i++) { <p>Foo</p> } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesMarkupStatementOnSwitchCharacterFollowedByColon()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@if(foo) { @:Bar" + Environment.NewLine
|
||||
+ "} zoop");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesMarkupStatementOnSwitchCharacterFollowedByDoubleColon()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@if(foo) { @::Sometext" + Environment.NewLine
|
||||
+ "}");
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void ParsesMarkupStatementOnSwitchCharacterFollowedByTripleColon()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@if(foo) { @:::Sometext" + Environment.NewLine
|
||||
+ "}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesMarkupStatementOnSwitchCharacterFollowedByColonInCodeBlock()
|
||||
{
|
||||
// Arrange
|
||||
ParseDocumentTest("@{ if(foo) { @:Bar" + Environment.NewLine
|
||||
+ "} } zoop");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyReturnsFromMarkupBlockWithPseudoTag()
|
||||
{
|
||||
ParseDocumentTest("@if (i > 0) { <text>;</text> }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CorrectlyReturnsFromMarkupBlockWithPseudoTagInCodeBlock()
|
||||
{
|
||||
ParseDocumentTest("@{ if (i > 0) { <text>;</text> } }");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsAllKindsOfImplicitMarkupInCodeBlock()
|
||||
{
|
||||
ParseDocumentTest("@{" + Environment.NewLine
|
||||
+ " if(true) {" + Environment.NewLine
|
||||
+ " @:Single Line Markup" + Environment.NewLine
|
||||
+ " }" + Environment.NewLine
|
||||
+ " foreach (var p in Enumerable.Range(1, 10)) {" + Environment.NewLine
|
||||
+ " <text>The number is @p</text>" + Environment.NewLine
|
||||
+ " }" + Environment.NewLine
|
||||
+ " if(!false) {" + Environment.NewLine
|
||||
+ " <p>A real tag!</p>" + Environment.NewLine
|
||||
+ " }" + Environment.NewLine
|
||||
+ "}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue