|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests;
public class ComponentCodeGenerationTestBase(bool designTime = false)
: RazorBaselineIntegrationTestBase(layer: TestProject.Layer.Compiler)
{
private RazorConfiguration _configuration;
internal override RazorFileKind? FileKind => RazorFileKind.Component;
internal override bool UseTwoPhaseCompilation => true;
internal override RazorConfiguration Configuration => _configuration ?? base.Configuration;
internal string ComponentName = "TestComponent";
internal override string DefaultFileName => ComponentName + ".cshtml";
internal override bool DesignTime => designTime;
protected override string GetDirectoryPath(string testName)
{
var directory = DesignTime ? "ComponentDesignTimeCodeGenerationTest" : "ComponentRuntimeCodeGenerationTest";
return $"TestFiles/IntegrationTests/{directory}/{testName}";
}
#region Basics
[IntegrationTestFact]
public void SingleLineControlFlowStatements_InCodeDirective()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Rendering;
@code {
void RenderChildComponent(RenderTreeBuilder __builder)
{
var output = string.Empty;
if (__builder == null) output = ""Builder is null!"";
else output = ""Builder is not null!"";
<p>Output: @output</p>
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void SingleLineControlFlowStatements_InCodeBlock()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.RenderTree;
@{
var output = string.Empty;
if (__builder == null) output = ""Builder is null!"";
else output = ""Builder is not null!"";
<p>Output: @output</p>
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_InFunctionsDirective()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Rendering;
@{ RenderChildComponent(__builder); }
@code {
void RenderChildComponent(RenderTreeBuilder __builder)
{
<MyComponent />
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_InLocalFunction()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.RenderTree;
@{
void RenderChildComponent()
{
<MyComponent />
}
}
@{ RenderChildComponent(); }
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_Simple()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_WithParameters()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class SomeType
{
}
public class MyComponent : ComponentBase
{
[Parameter] public int IntProperty { get; set; }
[Parameter] public bool BoolProperty { get; set; }
[Parameter] public string StringProperty { get; set; }
[Parameter] public SomeType ObjectProperty { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent
IntProperty=""123""
BoolProperty=""true""
StringProperty=""My string""
ObjectProperty=""new SomeType()""/>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ComponentWithDecimalParameter()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<strong>@TestDecimal</strong>
<TestComponent TestDecimal=""4"" />
@code {
[Parameter]
public decimal TestDecimal { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ComponentWithBooleanParameter()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<strong>@TestBool</strong>
<TestComponent TestBool=""true"" />
@code {
[Parameter]
public bool TestBool { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ComponentWithBooleanParameter_Minimized()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<strong>@TestBool</strong>
<TestComponent TestBool />
@code {
[Parameter]
public bool TestBool { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ComponentWithDynamicParameter()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<strong>@TestDynamic</strong>
<TestComponent TestDynamic=""4"" />
@code {
[Parameter]
public dynamic TestDynamic { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ComponentWithTypeParameters()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components;
@typeparam TItem1
@typeparam TItem2
<h1>Item1</h1>
@foreach (var item2 in Items2)
{
<p>
@ChildContent(item2);
</p>
}
@code {
[Parameter] public TItem1 Item1 { get; set; }
[Parameter] public List<TItem2> Items2 { get; set; }
[Parameter] public RenderFragment<TItem2> ChildContent { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/8711")]
public void ComponentWithTypeParameters_Interconnected()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
public class C<T> { }
public class D<T1, T2> where T1 : C<T2> { }
"""));
// Act
var generated = CompileToCSharp("""
@typeparam T1 where T1 : C<T2>
@typeparam T2 where T2 : D<T1, T2>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithEscapedParameterName()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int @class { get; set; }
[Parameter]
public int Prop2 { get; set; }
}
}
"""));
// Act
var generated = CompileToCSharp("""
<MyComponent class="1" Prop2="2">
</MyComponent>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithWriteOnlyParameter()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Prop { set { _ = value; } }
}
}
"""));
// Act
var generated = CompileToCSharp("""
<MyComponent Prop="1">
</MyComponent>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithInitOnlyParameter()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
namespace System.Runtime.CompilerServices;
internal static class IsExternalInit
{
}
"""));
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Prop { get; init; }
}
}
"""));
// Act
var generated = CompileToCSharp("""
<MyComponent Prop="1">
</MyComponent>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ComponentWithTypeParameters_WithSemicolon()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components;
@typeparam TItem1;
@typeparam TItem2;
<h1>Item1</h1>
@foreach (var item2 in Items2)
{
<p>
@ChildContent(item2);
</p>
}
@code {
[Parameter] public TItem1 Item1 { get; set; }
[Parameter] public List<TItem2> Items2 { get; set; }
[Parameter] public RenderFragment<TItem2> ChildContent { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ComponentWithTypeParameterArray()
{
// Arrange
var classes = @"
public class Tag
{
public string description { get; set; }
}
";
AdditionalSyntaxTrees.Add(Parse(classes));
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components;
@typeparam TItem
<h1>Item</h1>
<p>@ChildContent(Items1)</p>
@foreach (var item in Items2)
{
<p>@ChildContent(item)</p>
}
<p>@ChildContent(Items3())</p>
@code {
[Parameter] public TItem[] Items1 { get; set; }
[Parameter] public List<TItem[]> Items2 { get; set; }
[Parameter] public Func<TItem[]> Items3 { get; set; }
[Parameter] public RenderFragment<TItem[]> ChildContent { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
AdditionalSyntaxTrees.Add(Parse(generated.CodeDocument.GetRequiredCSharpDocument().Text));
var useGenerated = CompileToCSharp("UseTestComponent.cshtml", cshtmlContent: @"
@using Test
<TestComponent Items1=items1 Items2=items2 Items3=items3>
<p>@context[0].description</p>
</TestComponent>
@code {
static Tag tag = new Tag() { description = ""A description.""};
Tag[] items1 = new [] { tag };
List<Tag[]> items2 = new List<Tag[]>() { new [] { tag } };
Tag[] items3() => new [] { tag };
}");
AssertDocumentNodeMatchesBaseline(useGenerated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(useGenerated.CodeDocument);
CompileToAssembly(useGenerated);
}
[IntegrationTestFact]
public void ComponentWithTupleParameter()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@code {
[Parameter] public (int Horizontal, int Vertical) Gutter { get; set; }
}
<TestComponent Gutter=""(32, 16)"">
</TestComponent>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ComponentWithTypeParameterValueTuple()
{
// Arrange
var classes = @"
public class Tag
{
public string description { get; set; }
}
";
AdditionalSyntaxTrees.Add(Parse(classes));
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components;
@typeparam TItem1
@typeparam TItem2
<h1>Item</h1>
<p>@ChildContent(Item1)</p>
@foreach (var item in Items2)
{
<p>@ChildContent(item)</p>
}
@code {
[Parameter] public (TItem1, TItem2) Item1 { get; set; }
[Parameter] public List<(TItem1, TItem2)> Items2 { get; set; }
[Parameter] public RenderFragment<(TItem1, TItem2)> ChildContent { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
AdditionalSyntaxTrees.Add(Parse(generated.CodeDocument.GetRequiredCSharpDocument().Text));
var useGenerated = CompileToCSharp("UseTestComponent.cshtml", cshtmlContent: @"
@using Test
<TestComponent Item1=item1 Items2=items2>
<p>@context</p>
</TestComponent>
@code {
(string, int) item1 = (""A string"", 42);
static (string, int) item2 = (""Another string"", 42);
List<(string, int)> items2 = new List<(string, int)>() { item2 };
}");
AssertDocumentNodeMatchesBaseline(useGenerated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(useGenerated.CodeDocument);
CompileToAssembly(useGenerated);
}
[IntegrationTestFact]
public void ComponentWithTypeParameterValueTupleGloballyQualifiedTypes()
{
// Arrange
var classes = @"
namespace N;
public class MyClass
{
public int MyClassId { get; set; }
}
public struct MyStruct
{
public int MyStructId { get; set; }
}
";
AdditionalSyntaxTrees.Add(Parse(classes));
// Act
var generated = CompileToCSharp(@"
@using N
@typeparam TParam
@code {
[Parameter]
public TParam InferParam { get; set; }
[Parameter]
public RenderFragment<(MyClass I1, MyStruct I2, TParam P)> Template { get; set; }
}
<TestComponent InferParam=""1"">
<Template>
@context.I1.MyClassId - @context.I2.MyStructId
</Template>
</TestComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/7628")]
public void ComponentWithTypeParameterValueTuple_ExplicitGenericArguments()
{
// Act
var generated = CompileToCSharp("""
@typeparam TDomain where TDomain : struct
@typeparam TValue where TValue : struct
<TestComponent Data="null" TDomain="decimal" TValue="decimal" />
@code {
[Parameter]
public List<(TDomain Domain, TValue Value)> Data { get; set; }
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ComponentWithConstrainedTypeParameters()
{
// Arrange
var classes = @"
public class Image
{
public string url { get; set; }
public int id { get; set; }
public Image()
{
url = ""https://example.com/default.png"";
id = 1;
}
}
public interface ITag
{
string description { get; set; }
}
public class Tag : ITag
{
public string description { get; set; }
}
";
AdditionalSyntaxTrees.Add(Parse(classes));
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components;
@typeparam TItem1 where TItem1 : Image
@typeparam TItem2 where TItem2 : ITag
@typeparam TItem3 where TItem3 : Image, new()
<h1>Item1</h1>
@foreach (var item2 in Items2)
{
<p>
@ChildContent(item2);
</p>
}
<p>Item3</p>
@code {
[Parameter] public TItem1 Item1 { get; set; }
[Parameter] public List<TItem2> Items2 { get; set; }
[Parameter] public TItem3 Item3 { get; set; }
[Parameter] public RenderFragment<TItem2> ChildContent { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
AdditionalSyntaxTrees.Add(Parse(generated.CodeDocument.GetRequiredCSharpDocument().Text));
var useGenerated = CompileToCSharp("UseTestComponent.cshtml", cshtmlContent: @"
@using Test
<TestComponent Item1=@item1 Items2=@items Item3=@item1>
<p>@context</p>
</TestComponent>
@code {
Image item1 = new Image() { id = 1, url=""https://example.com""};
static Tag tag1 = new Tag() { description = ""A description.""};
static Tag tag2 = new Tag() { description = ""Another description.""};
List<Tag> items = new List<Tag>() { tag1, tag2 };
}");
AssertDocumentNodeMatchesBaseline(useGenerated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(useGenerated.CodeDocument);
CompileToAssembly(useGenerated);
}
[IntegrationTestFact]
public void ComponentWithConstrainedTypeParameters_WithSemicolon()
{
// Arrange
var classes = @"
public class Image
{
public string url { get; set; }
public int id { get; set; }
public Image()
{
url = ""https://example.com/default.png"";
id = 1;
}
}
public interface ITag
{
string description { get; set; }
}
public class Tag : ITag
{
public string description { get; set; }
}
";
AdditionalSyntaxTrees.Add(Parse(classes));
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components;
@typeparam TItem1 where TItem1 : Image;
@typeparam TItem2 where TItem2 : ITag;
@typeparam TItem3 where TItem3 : Image, new();
<h1>Item1</h1>
@foreach (var item2 in Items2)
{
<p>
@ChildContent(item2);
</p>
}
<p>Item3</p>
@code {
[Parameter] public TItem1 Item1 { get; set; }
[Parameter] public List<TItem2> Items2 { get; set; }
[Parameter] public TItem3 Item3 { get; set; }
[Parameter] public RenderFragment<TItem2> ChildContent { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
AdditionalSyntaxTrees.Add(Parse(generated.CodeDocument.GetRequiredCSharpDocument().Text));
var useGenerated = CompileToCSharp("UseTestComponent.cshtml", cshtmlContent: @"
@using Test
<TestComponent Item1=@item1 Items2=@items Item3=@item1>
<p>@context</p>
</TestComponent>
@code {
Image item1 = new Image() { id = 1, url=""https://example.com""};
static Tag tag1 = new Tag() { description = ""A description.""};
static Tag tag2 = new Tag() { description = ""Another description.""};
List<Tag> items = new List<Tag>() { tag1, tag2 };
}");
AssertDocumentNodeMatchesBaseline(useGenerated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(useGenerated.CodeDocument);
CompileToAssembly(useGenerated);
}
[IntegrationTestFact]
public void ChildComponent_WithExplicitStringParameter()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public string StringProperty { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent StringProperty=""@(42.ToString())"" />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_WithNonPropertyAttributes()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent some-attribute=""foo"" another-attribute=""@(43.ToString())""/>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ComponentParameter_TypeMismatch_ReportsDiagnostic()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class CoolnessMeter : ComponentBase
{
[Parameter] public int Coolness { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<CoolnessMeter Coolness=""@(""very-cool"")"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(1,28): error CS1503: Argument 1: cannot convert from 'string' to 'int'
// "very-cool"
Diagnostic(ErrorCode.ERR_BadArgType, @"""very-cool""").WithArguments("1", "string", "int").WithLocation(1, 28));
}
[IntegrationTestFact]
public void DataDashAttribute_ImplicitExpression()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@{
var myValue = ""Expression value"";
}
<elem data-abc=""Literal value"" data-def=""@myValue"" />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void DataDashAttribute_ExplicitExpression()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@{
var myValue = ""Expression value"";
}
<elem data-abc=""Literal value"" data-def=""@(myValue)"" />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void MarkupComment_IsNotIncluded()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@{
var myValue = ""Expression value"";
}
<div>@myValue <!-- @myValue --> </div>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void OmitsMinimizedAttributeValueParameter()
{
// Act
var generated = CompileToCSharp(@"
<elem normal-attr=""@(""val"")"" minimized-attr empty-string-atttr=""""></elem>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void IncludesMinimizedAttributeValueParameterBeforeLanguageVersion5()
{
// Arrange
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_3_0 };
// Act
var generated = CompileToCSharp(@"
<elem normal-attr=""@(""val"")"" minimized-attr empty-string-atttr=""""></elem>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithFullyQualifiedTagNames()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
namespace Test2
{
public class MyComponent2 : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent />
<Test.MyComponent />
<Test2.MyComponent2 />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithNullableActionParameter()
{
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class ComponentWithNullableAction : ComponentBase
{
[Parameter] public Action NullableAction { get; set; }
}
}
"));
var generated = CompileToCSharp(@"
<ComponentWithNullableAction NullableAction=""@NullableAction"" />
@code {
[Parameter]
public Action NullableAction { get; set; }
}
");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithNullableRenderFragmentParameter()
{
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class ComponentWithNullableRenderFragment : ComponentBase
{
[Parameter] public RenderFragment Header { get; set; }
}
}
"));
var generated = CompileToCSharp(@"
<ComponentWithNullableRenderFragment Header=""@Header"" />
@code {
[Parameter] public RenderFragment Header { get; set; }
}
");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithEditorRequiredParameter_NoValueSpecified()
{
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter]
[EditorRequired]
public string Property1 { get; set; }
}
}
"));
var generated = CompileToCSharp(@"
<ComponentWithEditorRequiredParameters />
");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
var diagnostics = Assert.Single(generated.RazorDiagnostics);
Assert.Equal(RazorDiagnosticSeverity.Warning, diagnostics.Severity);
Assert.Equal("RZ2012", diagnostics.Id);
}
[IntegrationTestFact]
public void Component_WithEditorRequiredParameter_ValueSpecified()
{
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter]
[EditorRequired]
public string Property1 { get; set; }
}
}
"));
var generated = CompileToCSharp(@"
<ComponentWithEditorRequiredParameters Property1=""Some Value"" />
");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
Assert.Empty(generated.RazorDiagnostics);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")]
public void Component_WithEditorRequiredParameter_ValueSpecified_EventCallbackRequired()
{
AdditionalSyntaxTrees.Add(Parse("""
using System;
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter, EditorRequired]
public bool Property1 { get; set; }
[Parameter, EditorRequired]
public EventCallback<bool> Property1Changed { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters Property1="false" />
""");
CompileToAssembly(generated);
generated.RazorDiagnostics.Verify(
// x:\dir\subdir\Test\TestComponent.cshtml(1,1): warning RZ2012: Component 'ComponentWithEditorRequiredParameters' expects a value for the parameter 'Property1Changed', but a value may not have been provided.
Diagnostic("RZ2012").WithLocation(1, 1));
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")]
public void Component_WithEditorRequiredParameter_ValueSpecified_ExpressionRequired()
{
AdditionalSyntaxTrees.Add(Parse("""
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter, EditorRequired]
public bool Property1 { get; set; }
[Parameter, EditorRequired]
public EventCallback<bool> Property1Changed { get; set; }
[Parameter, EditorRequired]
public Expression<Func<bool>> Property1Expression { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters Property1="false" />
""");
CompileToAssembly(generated);
generated.RazorDiagnostics.Verify(
// x:\dir\subdir\Test\TestComponent.cshtml(1,1): warning RZ2012: Component 'ComponentWithEditorRequiredParameters' expects a value for the parameter 'Property1Changed', but a value may not have been provided.
Diagnostic("RZ2012").WithLocation(1, 1),
// x:\dir\subdir\Test\TestComponent.cshtml(1,1): warning RZ2012: Component 'ComponentWithEditorRequiredParameters' expects a value for the parameter 'Property1Expression', but a value may not have been provided.
Diagnostic("RZ2012").WithLocation(1, 1));
}
[IntegrationTestFact]
public void Component_WithEditorRequiredParameter_ValueSpecified_DifferentCasing()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter, EditorRequired] public string Property1 { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters property1="Some Value" />
""");
generated.RazorDiagnostics.Verify(
// x:\dir\subdir\Test\TestComponent.cshtml(1,1): warning RZ2012: Component 'ComponentWithEditorRequiredParameters' expects a value for the parameter 'Property1', but a value may not have been provided.
Diagnostic("RZ2012").WithLocation(1, 1));
}
[IntegrationTestFact]
public void Component_WithEditorRequiredParameter_ValuesSpecifiedUsingSplatting()
{
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter]
[EditorRequired]
public string Property1 { get; set; }
}
}
"));
var generated = CompileToCSharp(@"
<ComponentWithEditorRequiredParameters @attributes=""@(new Dictionary<string, object>())"" />
");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
Assert.Empty(generated.RazorDiagnostics);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/7395")]
public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBind()
{
AdditionalSyntaxTrees.Add(Parse("""
using System;
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter]
[EditorRequired]
public string Property1 { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters @bind-Property1="myField" />
@code {
private string myField = "Some Value";
}
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
Assert.Empty(generated.RazorDiagnostics);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")]
public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBind_EventCallbackRequired()
{
AdditionalSyntaxTrees.Add(Parse("""
using System;
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter, EditorRequired]
public string Property1 { get; set; }
[Parameter, EditorRequired]
public EventCallback<string> Property1Changed { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters @bind-Property1="myField" />
@code {
private string myField = "Some Value";
}
""");
CompileToAssembly(generated);
generated.RazorDiagnostics.Verify();
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")]
public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBind_ExpressionRequired()
{
AdditionalSyntaxTrees.Add(Parse("""
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter, EditorRequired]
public string Property1 { get; set; }
[Parameter, EditorRequired]
public EventCallback<string> Property1Changed { get; set; }
[Parameter, EditorRequired]
public Expression<Func<string>> Property1Expression { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters @bind-Property1="myField" />
@code {
private string myField = "Some Value";
}
""");
CompileToAssembly(generated);
generated.RazorDiagnostics.Verify();
}
[IntegrationTestFact]
public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBind_DifferentCasing()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter, EditorRequired] public string Property1 { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters @bind-property1="Some Value" />
""");
generated.RazorDiagnostics.Verify(
// x:\dir\subdir\Test\TestComponent.cshtml(1,1): warning RZ2012: Component 'ComponentWithEditorRequiredParameters' expects a value for the parameter 'Property1', but a value may not have been provided.
Diagnostic("RZ2012").WithLocation(1, 1));
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")]
public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindGetSet()
{
AdditionalSyntaxTrees.Add(Parse("""
using System;
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter]
[EditorRequired]
public string Property1 { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters @bind-Property1:get="myField" @bind-Property1:set="OnFieldChanged" />
@code {
private string myField = "Some Value";
private void OnFieldChanged(string value) { }
}
""");
CompileToAssembly(generated);
Assert.Empty(generated.RazorDiagnostics);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")]
public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindGetSet_EventCallbackRequired()
{
AdditionalSyntaxTrees.Add(Parse("""
using System;
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter, EditorRequired]
public string Property1 { get; set; }
[Parameter, EditorRequired]
public EventCallback<string> Property1Changed { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters @bind-Property1:get="myField" @bind-Property1:set="OnFieldChanged" />
@code {
private string myField = "Some Value";
private void OnFieldChanged(string value) { }
}
""");
CompileToAssembly(generated);
generated.RazorDiagnostics.Verify();
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")]
public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindGetSet_ExpressionRequired()
{
AdditionalSyntaxTrees.Add(Parse("""
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter, EditorRequired]
public string Property1 { get; set; }
[Parameter, EditorRequired]
public EventCallback<string> Property1Changed { get; set; }
[Parameter, EditorRequired]
public Expression<Func<string>> Property1Expression { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters @bind-Property1:get="myField" @bind-Property1:set="OnFieldChanged" />
@code {
private string myField = "Some Value";
private void OnFieldChanged(string value) { }
}
""");
CompileToAssembly(generated);
generated.RazorDiagnostics.Verify();
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")]
public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindGet()
{
AdditionalSyntaxTrees.Add(Parse("""
using System;
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter]
[EditorRequired]
public string Property1 { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters @bind-Property1:get="myField" />
@code {
private string myField = "Some Value";
}
""");
CompileToAssembly(generated);
Assert.Empty(generated.RazorDiagnostics);
}
[IntegrationTestFact]
public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindGet_DifferentCasing()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter, EditorRequired] public string Property1 { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters @bind-property1:get="myField" />
@code {
private string myField = "Some Value";
}
""");
generated.RazorDiagnostics.Verify(
// x:\dir\subdir\Test\TestComponent.cshtml(1,1): warning RZ2012: Component 'ComponentWithEditorRequiredParameters' expects a value for the parameter 'Property1', but a value may not have been provided.
Diagnostic("RZ2012").WithLocation(1, 1));
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")]
public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindSet()
{
AdditionalSyntaxTrees.Add(Parse("""
using System;
using Microsoft.AspNetCore.Components;
namespace Test;
public class ComponentWithEditorRequiredParameters : ComponentBase
{
[Parameter]
[EditorRequired]
public string Property1 { get; set; }
}
"""));
var generated = CompileToCSharp("""
<ComponentWithEditorRequiredParameters @bind-Property1:set="OnFieldChanged" />
@code {
private void OnFieldChanged(string value) { }
}
""");
var compiled = CompileToAssembly(generated);
generated.RazorDiagnostics.Verify(
// x:\dir\subdir\Test\TestComponent.cshtml(1,61): error RZ10016: Attribute 'bind-Property1:set' was used but no attribute 'bind-Property1:get' was found.
Diagnostic("RZ10016").WithLocation(1, 61));
}
[IntegrationTestFact]
public void Component_WithEditorRequiredChildContent_NoValueSpecified()
{
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class ComponentWithEditorRequiredChildContent : ComponentBase
{
[Parameter]
[EditorRequired]
public RenderFragment ChildContent { get; set; }
}
}
"));
var generated = CompileToCSharp(@"
<ComponentWithEditorRequiredChildContent />
");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
var diagnostics = Assert.Single(generated.RazorDiagnostics);
Assert.Equal(RazorDiagnosticSeverity.Warning, diagnostics.Severity);
Assert.Equal("RZ2012", diagnostics.Id);
}
[IntegrationTestFact]
public void Component_WithEditorRequiredChildContent_ValueSpecified_WithoutName()
{
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class ComponentWithEditorRequiredChildContent : ComponentBase
{
[Parameter]
[EditorRequired]
public RenderFragment ChildContent { get; set; }
}
}
"));
var generated = CompileToCSharp(@"
<ComponentWithEditorRequiredChildContent>
<h1>Hello World</h1>
</ComponentWithEditorRequiredChildContent>
");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
Assert.Empty(generated.RazorDiagnostics);
}
[IntegrationTestFact]
public void Component_WithEditorRequiredChildContent_ValueSpecifiedAsText_WithoutName()
{
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class ComponentWithEditorRequiredChildContent : ComponentBase
{
[Parameter]
[EditorRequired]
public RenderFragment ChildContent { get; set; }
}
}
"));
var generated = CompileToCSharp(@"
<ComponentWithEditorRequiredChildContent>This is some text</ComponentWithEditorRequiredChildContent>
");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
Assert.Empty(generated.RazorDiagnostics);
}
[IntegrationTestFact]
public void Component_WithEditorRequiredChildContent_ValueSpecified()
{
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class ComponentWithEditorRequiredChildContent : ComponentBase
{
[Parameter]
[EditorRequired]
public RenderFragment ChildContent { get; set; }
}
}
"));
var generated = CompileToCSharp(@"
<ComponentWithEditorRequiredChildContent>
<ChildContent>
<h1>Hello World</h1>
</ChildContent>
</ComponentWithEditorRequiredChildContent>
");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
Assert.Empty(generated.RazorDiagnostics);
}
[IntegrationTestFact]
public void Component_WithEditorRequiredNamedChildContent_NoValueSpecified()
{
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class ComponentWithEditorRequiredChildContent : ComponentBase
{
[Parameter]
[EditorRequired]
public RenderFragment Found { get; set; }
[Parameter]
public RenderFragment NotFound { get; set; }
}
}
"));
var generated = CompileToCSharp(@"
<ComponentWithEditorRequiredChildContent>
</ComponentWithEditorRequiredChildContent>
");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
var diagnostics = Assert.Single(generated.RazorDiagnostics);
Assert.Equal(RazorDiagnosticSeverity.Warning, diagnostics.Severity);
Assert.Equal("RZ2012", diagnostics.Id);
}
[IntegrationTestFact]
public void Component_WithEditorRequiredNamedChildContent_ValueSpecified()
{
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class ComponentWithEditorRequiredChildContent : ComponentBase
{
[Parameter]
[EditorRequired]
public RenderFragment Found { get; set; }
[Parameter]
public RenderFragment NotFound { get; set; }
}
}
"));
var generated = CompileToCSharp(@"
<ComponentWithEditorRequiredChildContent>
<Found><h1>Here's Johnny!</h1></Found>
</ComponentWithEditorRequiredChildContent>
");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
Assert.Empty(generated.RazorDiagnostics);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/18042")]
public void AddAttribute_ImplicitStringConversion_TypeInference()
{
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 };
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyClass<T>
{
public static implicit operator string(MyClass<T> c) => throw null!;
}
public class MyComponent<T> : ComponentBase
{
[Parameter]
public MyClass<T> MyParameter { get; set; } = null!;
[Parameter]
public bool BoolParameter { get; set; }
[Parameter]
public string StringParameter { get; set; } = null!;
[Parameter]
public System.Delegate DelegateParameter { get; set; } = null!;
[Parameter]
public object ObjectParameter { get; set; } = null!;
}
"""));
var generated = CompileToCSharp("""
<MyComponent MyParameter="c"
BoolParameter="true"
StringParameter="str"
DelegateParameter="() => { }"
ObjectParameter="c" />
@code {
private readonly MyClass<string> c = new();
}
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/18042")]
public void AddAttribute_ImplicitStringConversion_Bind()
{
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 };
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyClass<T>
{
public static implicit operator string(MyClass<T> c) => throw null!;
}
public class MyComponent<T> : ComponentBase
{
[Parameter]
public MyClass<T> MyParameter { get; set; }
[Parameter]
public EventCallback<MyClass<T>> MyParameterChanged { get; set; }
[Parameter]
public bool BoolParameter { get; set; }
[Parameter]
public string StringParameter { get; set; } = null!;
[Parameter]
public System.Delegate DelegateParameter { get; set; } = null!;
[Parameter]
public object ObjectParameter { get; set; } = null!;
}
"""));
var generated = CompileToCSharp("""
<MyComponent @bind-MyParameter="c"
BoolParameter="true"
StringParameter="str"
DelegateParameter="() => { }"
ObjectParameter="c" />
@code {
private MyClass<string> c = new();
}
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/18042")]
public void AddAttribute_ImplicitStringConversion_CustomEvent()
{
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 };
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyClass<T>
{
public static implicit operator string(MyClass<T> c) => throw null!;
}
public class MyComponent<T> : ComponentBase
{
[Parameter]
public MyClass<T> MyParameter { get; set; }
[Parameter]
public EventCallback MyEvent { get; set; }
[Parameter]
public bool BoolParameter { get; set; }
[Parameter]
public string StringParameter { get; set; } = null!;
[Parameter]
public System.Delegate DelegateParameter { get; set; } = null!;
[Parameter]
public object ObjectParameter { get; set; } = null!;
}
"""));
var generated = CompileToCSharp("""
<MyComponent MyParameter="c"
MyEvent="() => { }"
BoolParameter="true"
StringParameter="str"
DelegateParameter="() => { }"
ObjectParameter="c" />
@code {
private MyClass<string> c = new();
}
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/18042")]
public void AddAttribute_ImplicitStringConversion_BindUnknown()
{
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 };
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyClass
{
public static implicit operator string(MyClass c) => throw null!;
}
public class MyComponent : ComponentBase
{
}
"""));
var generated = CompileToCSharp("""
<MyComponent @bind-Value="c" />
@code {
private MyClass c = new();
}
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/18042")]
public void AddAttribute_ImplicitStringConversion_BindUnknown_Assignment()
{
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 };
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyClass
{
public static implicit operator string(MyClass c) => throw null!;
}
public class MyComponent : ComponentBase
{
}
"""));
var generated = CompileToCSharp("""
<MyComponent @bind-Value="c1 = c2" />
@code {
private MyClass c1 = new();
private MyClass c2 = new();
}
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/18042")]
public void AddAttribute_ImplicitBooleanConversion()
{
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 };
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyClass<T>
{
public static implicit operator bool(MyClass<T> c) => throw null!;
}
public class MyComponent<T> : ComponentBase
{
[Parameter]
public MyClass<T> MyParameter { get; set; }
[Parameter]
public bool BoolParameter { get; set; }
}
"""));
var generated = CompileToCSharp("""
<MyComponent MyParameter="c" BoolParameter="c" />
@code {
private MyClass<string> c = new();
}
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/48778")]
public void ImplicitStringConversion_ParameterCasing_AddAttribute()
{
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 };
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyClass
{
public static implicit operator string(MyClass c) => "";
}
public class MyComponent : ComponentBase
{
[Parameter] public string Placeholder { get; set; } = "";
}
"""));
var generated = CompileToCSharp("""
<MyComponent PlaceHolder="@(new MyClass())" />
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/48778")]
public void ImplicitStringConversion_ParameterCasing_AddComponentParameter()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyClass
{
public static implicit operator string(MyClass c) => "";
}
public class MyComponent : ComponentBase
{
[Parameter] public string Placeholder { get; set; } = "";
}
"""));
var generated = CompileToCSharp("""
<MyComponent PlaceHolder="@(new MyClass())" />
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/48778")]
public void ImplicitStringConversion_ParameterCasing_Multiple()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyClass
{
public static implicit operator string(MyClass c) => "";
}
public class MyComponent : ComponentBase
{
[Parameter] public string Placeholder { get; set; } = "";
[Parameter] public string PlaceHolder { get; set; } = "";
}
"""));
var generated = CompileToCSharp("""
<MyComponent PlaceHolder="@(new MyClass())" />
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/48778")]
public void ImplicitStringConversion_ParameterCasing_Bind()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyComponent : ComponentBase
{
[Parameter] public string Placeholder { get; set; } = "";
[Parameter] public EventCallback<string> PlaceholderChanged { get; set; }
}
"""));
var generated = CompileToCSharp("""
<MyComponent @bind-PlaceHolder="s" />
@code {
private string s = "abc";
}
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/48778")]
public void ImplicitStringConversion_ParameterCasing_Bind_02()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyComponent : ComponentBase
{
[Parameter] public string Placeholder { get; set; } = "";
[Parameter] public EventCallback<string> PlaceholderChanged { get; set; }
}
"""));
var generated = CompileToCSharp("""
<MyComponent @Bind-Placeholder="@s" />
@code {
private string s = "abc";
}
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/48778")]
public void ImplicitStringConversion_ParameterCasing_Bind_03()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyComponent : ComponentBase
{
[Parameter] public string Placeholder { get; set; } = "";
[Parameter] public EventCallback<string> PlaceholderChanged { get; set; }
}
"""));
var generated = CompileToCSharp("""
<MyComponent @bind-Placeholder:Get="s" @bind-Placeholder:set="Changed" />
@code {
private string s = "abc";
private void Changed(string s) { }
}
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1869483")]
public void AddComponentParameter()
{
var generated = CompileToCSharp("""
@typeparam T
<TestComponent Param="42" />
@code {
[Parameter]
public T Param { get; set; }
}
""");
CompileToAssembly(generated);
if (DesignTime)
{
// In design-time, AddComponentParameter shouldn't be used.
Assert.Contains("AddAttribute", generated.Code);
Assert.DoesNotContain("AddComponentParameter", generated.Code);
}
else
{
Assert.DoesNotContain("AddAttribute", generated.Code);
Assert.Contains("AddComponentParameter", generated.Code);
}
}
[IntegrationTestFact]
public void AddComponentParameter_GlobalNamespace()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
public class MyComponent : ComponentBase
{
[Parameter]
public string Value { get; set; }
}"));
// Act
var generated = CompileToCSharp(@"<MyComponent Value=""Hello"" />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void AddComponentParameter_WithNameof()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public string Value { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Value=""Hello"" />
@code {
public string nameof(string s) => string.Empty;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, designTime ? [] : [
// (21,55): error CS0120: An object reference is required for the non-static field, method, or property 'MyComponent.Value'
// __builder.AddComponentParameter(1, nameof(global::Test.MyComponent.
Diagnostic(ErrorCode.ERR_ObjectRequired, "global::Test.MyComponent.\r\n#nullable restore\r\n#line (1,14)-(1,19) \"x:\\dir\\subdir\\Test\\TestComponent.cshtml\"\r\nValue").WithArguments("Test.MyComponent.Value").WithLocation(21, 55)
]);
}
[IntegrationTestFact]
public void AddComponentParameter_EscapedComponentName()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class @int : ComponentBase
{
[Parameter]
public string Value { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"<int Value=""Hello"" />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void AddComponentParameter_DynamicComponentName()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class dynamic : ComponentBase
{
[Parameter]
public string Value { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"<dynamic Value=""Hello"" />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10965")]
public void InvalidCode_EmptyTransition()
{
// Act
var generated = CompileToCSharp("""
<TestComponent Value="Hello" />
@
@code {
[Parameter] public int Param { get; set; }
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, DesignTime ? [
// x:\dir\subdir\Test\TestComponent.cshtml(3,7): error CS1525: Invalid expression term ';'
// __o = ;
Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(3, 7)
] : [
// x:\dir\subdir\Test\TestComponent.cshtml(3,2): error CS1525: Invalid expression term ')'
// __builder.AddContent(3,
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments(")").WithLocation(3, 2)
]);
}
[IntegrationTestFact]
public void ExplicitExpression_HtmlOnly()
{
// Act
var generated = CompileToCSharp("""
@{
<p></p>
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ExplicitExpression_Whitespace()
{
// Act
var generated = CompileToCSharp("""
@{
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11551")]
public void LayoutDirective()
{
// Act
var generated = CompileToCSharp("""
@layout System.Object
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_AddContent_Multiline()
{
// Act
var generated = CompileToCSharp(""""
@(@"This
is
a
multiline
string")
"""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var result = CompileToAssembly(generated);
AssertSequencePointsMatchBaseline(result, generated.CodeDocument);
}
#endregion
#region Bind
[IntegrationTestFact]
public void BindToComponent_SpecifiesValue_WithMatchingProperties()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_SpecifiesValue_WithMatchingProperties_WithNameof()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
public string nameof(string s) => string.Empty;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, designTime ? [] : [
// (21,55): error CS0120: An object reference is required for the non-static field, method, or property 'MyComponent.Value'
// __builder.AddComponentParameter(1, nameof(global::Test.MyComponent.
Diagnostic(ErrorCode.ERR_ObjectRequired, "global::Test.MyComponent.\r\n#nullable restore\r\n#line (1,20)-(1,25) \"x:\\dir\\subdir\\Test\\TestComponent.cshtml\"\r\nValue").WithArguments("Test.MyComponent.Value").WithLocation(21, 55),
// (38,55): error CS0120: An object reference is required for the non-static field, method, or property 'MyComponent.ValueChanged'
// __builder.AddComponentParameter(2, nameof(global::Test.MyComponent.ValueChanged), (global::System.Action<System.Int32>)(__value => ParentValue = __value));
Diagnostic(ErrorCode.ERR_ObjectRequired, "global::Test.MyComponent.ValueChanged").WithArguments("Test.MyComponent.ValueChanged").WithLocation(38, 55)
]);
}
[IntegrationTestFact]
public void BindToComponent_SpecifiesValue_WithMatchingProperties_EscapedComponentName()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class @int : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<int @bind-Value=""ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_SpecifiesValue_WithMatchingProperties_DynamicComponentName()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class dynamic : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<dynamic @bind-Value=""ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithStringAttribute_DoesNotUseStringSyntax()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class InputText : ComponentBase
{
[Parameter]
public string Value { get; set; }
[Parameter]
public Action<string> ValueChanged { get; set; }
}
}"));
AdditionalSyntaxTrees.Add(Parse(@"
using System;
namespace Test
{
public class Person
{
public string Name { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<InputText @bind-Value=""person.Name"" />
@functions
{
Person person = new Person();
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_TypeChecked_WithMatchingProperties()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" />
@code {
public string ParentValue { get; set; } = ""42"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, DesignTime
? [// x:\dir\subdir\Test\TestComponent.cshtml(1,27): error CS1503: Argument 1: cannot convert from 'string' to 'int'
// ParentValue
Diagnostic(ErrorCode.ERR_BadArgType, "ParentValue").WithArguments("1", "string", "int").WithLocation(1, 27),
// (37,38): error CS0029: Cannot implicitly convert type 'int' to 'string'
// __builder.AddComponentParameter(2, "ValueChanged", (global::System.Action<System.Int32>)(__value => ParentValue = __value));
Diagnostic(ErrorCode.ERR_NoImplicitConv, "__value").WithArguments("int", "string").WithLocation(37, 38)]
: [// x:\dir\subdir\Test\TestComponent.cshtml(1,27): error CS1503: Argument 1: cannot convert from 'string' to 'int'
// ParentValue
Diagnostic(ErrorCode.ERR_BadArgType, "ParentValue").WithArguments("1", "string", "int").WithLocation(1, 27),
// (38,166): error CS0029: Cannot implicitly convert type 'int' to 'string'
// __builder.AddComponentParameter(2, nameof(global::Test.MyComponent.ValueChanged), (global::System.Action<global::System.Int32>)(__value => ParentValue = __value));
Diagnostic(ErrorCode.ERR_NoImplicitConv, "__value").WithArguments("int", "string").WithLocation(38, 166)]);
}
[IntegrationTestFact]
public void BindToComponent_EventCallback_SpecifiesValue_WithMatchingProperties()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_EventCallback_TypeChecked_WithMatchingProperties()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" />
@code {
public string ParentValue { get; set; } = ""42"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, DesignTime
? [// x:\dir\subdir\Test\TestComponent.cshtml(1,27): error CS1503: Argument 1: cannot convert from 'string' to 'int'
// ParentValue
Diagnostic(ErrorCode.ERR_BadArgType, "ParentValue").WithArguments("1", "string", "int").WithLocation(1, 27),
// (37,13): error CS1503: Argument 2: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<string>' to 'Microsoft.AspNetCore.Components.EventCallback'
// global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback(this, __value => ParentValue = __value, ParentValue)));
Diagnostic(ErrorCode.ERR_BadArgType, "global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback(this, __value => ParentValue = __value, ParentValue)").WithArguments("2", "Microsoft.AspNetCore.Components.EventCallback<string>", "Microsoft.AspNetCore.Components.EventCallback").WithLocation(37, 13)]
: [// x:\dir\subdir\Test\TestComponent.cshtml(1,27): error CS1503: Argument 1: cannot convert from 'string' to 'int'
// ParentValue
Diagnostic(ErrorCode.ERR_BadArgType, "ParentValue").WithArguments("1", "string", "int").WithLocation(1, 27),
// (38,351): error CS1503: Argument 2: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<string>' to 'Microsoft.AspNetCore.Components.EventCallback'
// global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback(this, __value => ParentValue = __value, ParentValue)));
Diagnostic(ErrorCode.ERR_BadArgType, "global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback(this, __value => ParentValue = __value, ParentValue)").WithArguments("2", "Microsoft.AspNetCore.Components.EventCallback<string>", "Microsoft.AspNetCore.Components.EventCallback").WithLocation(38, 351)
]
);
}
[IntegrationTestFact]
public void BindToComponent_SpecifiesValue_WithoutMatchingProperties()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_SpecifiesValueAndChangeEvent_WithMatchingProperties()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> OnChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" @bind-Value:event=""OnChanged"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_SpecifiesValueAndChangeEvent_WithoutMatchingProperties()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}"));
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" @bind-Value:event=""OnChanged"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_SpecifiesValueAndExpression()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
[Parameter]
public Expression<Func<int>> ValueExpression { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_EventCallback_SpecifiesValueAndExpression()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
[Parameter]
public Expression<Func<int>> ValueExpression { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_SpecifiesValueAndExpression_TypeChecked()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
[Parameter]
public Expression<Func<string>> ValueExpression { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
CompileToAssembly(generated, DesignTime
? [// (38,195): error CS0029: Cannot implicitly convert type 'int' to 'string'
// __o = global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<global::System.Linq.Expressions.Expression<global::System.Func<global::System.String>>>(() => ParentValue);
Diagnostic(ErrorCode.ERR_NoImplicitConv, "ParentValue").WithArguments("int", "string").WithLocation(38, 195),
// (38,195): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type
// __o = global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<global::System.Linq.Expressions.Expression<global::System.Func<global::System.String>>>(() => ParentValue);
Diagnostic(ErrorCode.ERR_CantConvAnonMethReturns, "ParentValue").WithArguments("lambda expression").WithLocation(38, 195)]
: [// (39,274): error CS0029: Cannot implicitly convert type 'int' to 'string'
// __builder.AddComponentParameter(3, nameof(global::Test.MyComponent.ValueExpression), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<global::System.Linq.Expressions.Expression<global::System.Func<global::System.String>>>(() => ParentValue));
Diagnostic(ErrorCode.ERR_NoImplicitConv, "ParentValue").WithArguments("int", "string").WithLocation(39, 274),
// (39,274): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type
// __builder.AddComponentParameter(3, nameof(global::Test.MyComponent.ValueExpression), global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<global::System.Linq.Expressions.Expression<global::System.Func<global::System.String>>>(() => ParentValue));
Diagnostic(ErrorCode.ERR_CantConvAnonMethReturns, "ParentValue").WithArguments("lambda expression").WithLocation(39, 274)
]);
}
[IntegrationTestFact]
public void BindToComponent_SpecifiesValueAndExpression_Generic()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<T> : ComponentBase
{
[Parameter]
public T SomeParam { get; set; }
[Parameter]
public Action<T> SomeParamChanged { get; set; }
[Parameter]
public Expression<Func<T>> SomeParamExpression { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-SomeParam=""ParentValue"" />
@code {
public DateTime ParentValue { get; set; } = DateTime.Now;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_EventCallback_SpecifiesValueAndExpression_Generic()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<T> : ComponentBase
{
[Parameter]
public T SomeParam { get; set; }
[Parameter]
public EventCallback<T> SomeParamChanged { get; set; }
[Parameter]
public Expression<Func<T>> SomeParamExpression { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-SomeParam=""ParentValue"" />
@code {
public DateTime ParentValue { get; set; } = DateTime.Now;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_EventCallback_SpecifiesValueAndExpression_NestedGeneric()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Linq.Expressions;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<T> : ComponentBase
{
[Parameter]
public IEnumerable<T> SomeParam { get; set; }
[Parameter]
public EventCallback<IEnumerable<T>> SomeParamChanged { get; set; }
[Parameter]
public Expression<Func<IEnumerable<T>>> SomeParamExpression { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-SomeParam=""ParentValue"" />
@code {
public IEnumerable<DateTime> ParentValue { get; set; } = new [] { DateTime.Now };
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10609")]
public void BindToComponent_SpecifiesValue_WithMatchingProperties_GlobalNamespaceComponent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value=""ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElement_WritesAttributes()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", null, ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind=""@ParentValue"" />
@code {
public string ParentValue { get; set; } = ""hi"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElement_WithoutCloseTag()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", null, ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div>
<input @bind=""@ParentValue"">
</div>
@code {
public string ParentValue { get; set; } = ""hi"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElement_WithBindAfterAndSuffix()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", ""myvalue"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind-myvalue=""@ParentValue"" @bind-myvalue:after=""DoSomething"">
</div>
@code {
public string ParentValue { get; set; } = ""hi"";
Task DoSomething()
{
return Task.CompletedTask;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElement_WithGetSetAndSuffix()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", ""myvalue"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind-myvalue:get=""@ParentValue"" @bind-myvalue:set=""ValueChanged"">
</div>
@code {
public string ParentValue { get; set; } = ""hi"";
Task ValueChanged(string value)
{
return Task.CompletedTask;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithGetSet_TaskReturningDelegate()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Func<int, Task> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public int ParentValue { get; set; } = 42;
public Task UpdateValue(int value) { ParentValue = value; return Task.CompletedTask; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithGetSet_TaskReturningLambda()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Func<int, Task> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""value => { ParentValue = value; return Task.CompletedTask; }"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithGetSet_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public int ParentValue { get; set; } = 42;
public void UpdateValue(int value) => ParentValue = value;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithGetSet_ActionLambda()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""value => ParentValue = value"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithGetSet_EventCallback()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public int ParentValue { get; set; } = 42;
public EventCallback<int> UpdateValue { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToGenericComponent_InferredType_WithGetSet_EventCallback()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
public class CustomValue
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public CustomValue ParentValue { get; set; } = new CustomValue();
public EventCallback<CustomValue> UpdateValue { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToGenericComponent_ExplicitType_WithGetSet_EventCallback()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
public class CustomValue
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TValue=""CustomValue"" @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public CustomValue ParentValue { get; set; } = new CustomValue();
public EventCallback<CustomValue> UpdateValue { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponentBindToGenericComponent_InferredType_WithGetSet_EventCallback()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
@typeparam TParam
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public TParam ParentValue { get; set; } = default;
public EventCallback<TParam> UpdateValue { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericBindToGenericComponent_ExplicitType_WithGetSet_EventCallback()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
@typeparam TParam
<MyComponent TValue=""TParam"" @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public TParam ParentValue { get; set; } = default;
public EventCallback<TParam> UpdateValue { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToGenericComponent_InferredType_WithGetSet_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
public class CustomValue
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public CustomValue ParentValue { get; set; } = new CustomValue();
public void UpdateValue(CustomValue value) => ParentValue = value;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToGenericComponent_InferredType_WithGetSet_Function()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
public class CustomValue
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public CustomValue ParentValue { get; set; } = new CustomValue();
public Task UpdateValue(CustomValue value) { ParentValue = value; return Task.CompletedTask; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToGenericComponent_ExplicitType_WithGetSet_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
public class CustomValue
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TValue=""CustomValue"" @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public CustomValue ParentValue { get; set; } = new CustomValue();
public void UpdateValue(CustomValue value) => ParentValue = value;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToGenericComponent_ExplicitType_WithGetSet_Function()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
public class CustomValue
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TValue=""CustomValue"" @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public CustomValue ParentValue { get; set; } = new CustomValue();
public Task UpdateValue(CustomValue value) { ParentValue = value; return Task.CompletedTask; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponentBindToGenericComponent_InferredType_WithGetSet_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
@typeparam TParam
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public TParam ParentValue { get; set; } = default;
public void UpdateValue(TParam value) { ParentValue = value; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponentBindToGenericComponent_InferredType_WithGetSet_Function()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
@typeparam TParam
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public TParam ParentValue { get; set; } = default;
public Task UpdateValue(TParam value) { ParentValue = value; return Task.CompletedTask; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericBindToGenericComponent_ExplicitType_WithGetSet_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
@typeparam TParam
<MyComponent TValue=""TParam"" @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public TParam ParentValue { get; set; } = default;
public void UpdateValue(TParam value) { ParentValue = value; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericBindToGenericComponent_ExplicitType_WithGetSet_Function()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public EventCallback<TValue> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
@typeparam TParam
<MyComponent TValue=""TParam"" @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public TParam ParentValue { get; set; } = default;
public Task UpdateValue(TParam value) { ParentValue = value; return Task.CompletedTask; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithGetSet_EventCallback_ReceivesAction()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""value => ParentValue = value"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithGetSet_EventCallback_ReceivesFunction()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public int ParentValue { get; set; } = 42;
public Task UpdateValue(int value) { ParentValue = value; return Task.CompletedTask; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithAfter_EventCallback()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:after=""UpdateValue"" />
@code {
public int ParentValue { get; set; } = 42;
public EventCallback UpdateValue { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(1,63): error CS1503: Argument 1: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback' to 'System.Action'
// UpdateValue
Diagnostic(ErrorCode.ERR_BadArgType, "UpdateValue").WithArguments("1", "Microsoft.AspNetCore.Components.EventCallback", "System.Action").WithLocation(1, 63));
}
[IntegrationTestFact]
public void BindToComponent_WithAfter_TaskReturningDelegate()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Func<int, Task> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:after=""Update"" />
@code {
public int ParentValue { get; set; } = 42;
public Task Update() => Task.CompletedTask;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithAfter_TaskReturningLambda()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Func<int, Task> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:after=""() => { return Task.CompletedTask; }"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithAfter_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:after=""Update"" />
@code {
public int ParentValue { get; set; } = 42;
public void Update() { }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToGenericComponent_InferredType_WithAfter_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public Action<TValue> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:after=""Update"" />
@code {
public int ParentValue { get; set; } = 42;
public void Update() { }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToGenericComponent_ExplicitType_WithAfter_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public Action<TValue> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TValue=""int"" @bind-Value:get=""ParentValue"" @bind-Value:after=""Update"" />
@code {
public int ParentValue { get; set; } = 42;
public void Update() { }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponentBindToGenericComponent_InferredType_WithAfter_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public Action<TValue> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
@typeparam TParam
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:after=""Update"" />
@code {
public TParam ParentValue { get; set; }
public void Update() { }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponentBindToGenericComponent_ExplicitType_WithAfter_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TValue> : ComponentBase
{
[Parameter]
public TValue Value { get; set; }
[Parameter]
public Action<TValue> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
@typeparam TParam
<MyComponent TValue=""TParam"" @bind-Value:get=""ParentValue"" @bind-Value:after=""Update"" />
@code {
public TParam ParentValue { get; set; }
public void Update() { }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithAfter_ActionLambda()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:after=""() => { }"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithAfter_EventCallback_ReceivesAction()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:after=""() => { }"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithAfter_EventCallback_ReceivesFunction()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:after=""UpdateValue"" />
@code {
public int ParentValue { get; set; } = 42;
public Task UpdateValue() => Task.CompletedTask;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToComponent_WithAfter_AsyncLambdaProducesError()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""(value => { ParentValue = value; return Task.CompletedTask; })"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(1,94): error CS8030: Anonymous function converted to a void returning delegate cannot return a value
// (value => { ParentValue = value; return Task.CompletedTask; })
Diagnostic(ErrorCode.ERR_RetNoObjectRequiredLambda, "return").WithLocation(1, 94));
}
[IntegrationTestFact]
public void BindToElement_WithStringAttribute_WritesAttributes()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind-value=""ParentValue"" />
@code {
public string ParentValue { get; set; } = ""hi"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElementWithSuffix_WritesAttributes()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind-value=""@ParentValue"" />
@code {
public string ParentValue { get; set; } = ""hi"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElementWithSuffix_OverridesEvent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind-value=""@ParentValue"" @bind-value:event=""anotherevent"" />
@code {
public string ParentValue { get; set; } = ""hi"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElement_WithEventAsExpression()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
@{ var x = ""anotherevent""; }
<div @bind-value=""@ParentValue"" @bind-value:event=""@x"" />
@code {
public string ParentValue { get; set; } = ""hi"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElement_WithEventAsExplicitExpression()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
@{ var x = ""anotherevent""; }
<div @bind-value=""@ParentValue"" @bind-value:event=""@(x.ToString())"" />
@code {
public string ParentValue { get; set; } = ""hi"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BuiltIn_BindToInputWithoutType_WritesAttributes()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<input @bind=""@ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BuiltIn_BindToInputWithoutType_IsCaseSensitive()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<input @BIND=""@ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BuiltIn_BindToInputText_WithFormat_WritesAttributes()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<input type=""text"" @bind=""@CurrentDate"" @bind:format=""MM/dd/yyyy""/>
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BuiltIn_BindToInputText_WithFormatFromProperty_WritesAttributes()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<input type=""text"" @bind=""@CurrentDate"" @bind:format=""@Format""/>
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
public string Format { get; set; } = ""MM/dd/yyyy"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BuiltIn_BindToInputText_WritesAttributes()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<input type=""text"" @bind=""@ParentValue"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BuiltIn_BindToInputCheckbox_WritesAttributes()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<input type=""checkbox"" @bind=""@Enabled"" />
@code {
public bool Enabled { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElementFallback_WritesAttributes()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<input type=""text"" @bind-value=""@ParentValue"" @bind-value:event=""onchange"" />
@code {
public int ParentValue { get; set; } = 42;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElementFallback_WithFormat_WritesAttributes()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<input type=""text"" @bind-value=""@CurrentDate"" @bind-value:event=""onchange"" @bind-value:format=""MM/dd"" />
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElementFallback_WithCulture()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using System.Globalization
<div @bind-value=""@ParentValue"" @bind-value:event=""onchange"" @bind-value:culture=""CultureInfo.InvariantCulture"" />
@code {
public string ParentValue { get; set; } = ""hi"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElementWithCulture()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
@using System.Globalization
<div @bind-value=""@ParentValue"" @bind-value:event=""anotherevent"" @bind-value:culture=""CultureInfo.InvariantCulture"" />
@code {
public string ParentValue { get; set; } = ""hi"";
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToInputElementWithDefaultCulture()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Test
{
[BindInputElement(""custom"", null, ""value"", ""onchange"", isInvariantCulture: true, format: null)]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
@using System.Globalization
<input type=""custom"" @bind-value=""@ParentValue"" @bind-value:event=""anotherevent"" />
@code {
public int ParentValue { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToInputElementWithDefaultCulture_Override()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Test
{
[BindInputElement(""custom"", null, ""value"", ""onchange"", isInvariantCulture: true, format: null)]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
@using System.Globalization
<input type=""custom"" @bind-value=""@ParentValue"" @bind-value:event=""anotherevent"" @bind-value:culture=""CultureInfo.CurrentCulture"" />
@code {
public int ParentValue { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BindToElement_MixingBindAndParamBindSet()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind-value=""@ParentValue"" @bind-value:set=""UpdateValue"" />
@code {
public string ParentValue { get; set; } = ""hi"";
public void UpdateValue(string value) => ParentValue = value;
}");
// Assert
Assert.Collection(generated.RazorDiagnostics,
diagnostic => Assert.Equal("RZ10015", diagnostic.Id));
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
}
[IntegrationTestFact]
public void BindToElement_MixingBindWithoutSuffixAndParamBindSetWithSuffix()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", null, ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind=""@ParentValue"" @bind-value:set=""UpdateValue"" />
@code {
public string ParentValue { get; set; } = ""hi"";
public void UpdateValue(string value) => ParentValue = value;
}");
// Assert
Assert.Collection(generated.RazorDiagnostics,
diagnostic => Assert.Equal("RZ10016", diagnostic.Id));
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
}
[IntegrationTestFact]
public void BindToElement_MixingBindValueWithGetSet()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", null, ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind=""@ParentValue"" @bind:get=""@ParentValue"" @bind:set=""UpdateValue"" />
@code {
public string ParentValue { get; set; } = ""hi"";
public void UpdateValue(string value) => ParentValue = value;
}");
// Assert
Assert.Collection(generated.RazorDiagnostics,
diagnostic => Assert.Equal("RZ10018", diagnostic.Id),
diagnostic => Assert.Equal("RZ10015", diagnostic.Id));
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
}
[IntegrationTestFact]
public void BindToComponent_WithGetSet_ProducesErrorOnOlderLanguageVersions()
{
_configuration = new(
RazorLanguageVersion.Version_6_0,
"unnamed",
Extensions: [],
UseConsolidatedMvcViews: false);
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public int Value { get; set; }
[Parameter]
public Action<int> ValueChanged { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Value:get=""ParentValue"" @bind-Value:set=""UpdateValue"" />
@code {
public int ParentValue { get; set; } = 42;
public void UpdateValue(int value) => ParentValue = value;
}");
// Assert
Assert.Collection(generated.RazorDiagnostics,
diagnostic => Assert.Equal("RZ10020", diagnostic.Id));
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
}
[IntegrationTestFact]
public void BindToElement_MixingSetWithAfter()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", null, ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind:get=""@ParentValue"" @bind:set=""UpdateValue"" @bind:after=""AfterUpdate"" />
@code {
public string ParentValue { get; set; } = ""hi"";
public void UpdateValue(string value) => ParentValue = value;
public void AfterUpdate() { }
}");
// Assert
Assert.Collection(generated.RazorDiagnostics,
diagnostic => Assert.Equal("RZ10019", diagnostic.Id));
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
}
[IntegrationTestFact]
public void BindToElement_MissingBindGet()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind-value:set=""UpdateValue"" />
@code {
public string ParentValue { get; set; } = ""hi"";
public void UpdateValue(string value) => ParentValue = value;
}");
// Assert
Assert.Collection(generated.RazorDiagnostics,
diagnostic => Assert.Equal("RZ10016", diagnostic.Id));
}
[IntegrationTestFact]
public void BindToElement_MissingBindSet()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<div @bind-value:get=""ParentValue"" />
@code {
public string ParentValue { get; set; } = ""hi"";
public void UpdateValue(string value) => ParentValue = value;
}");
// Assert
Assert.Collection(generated.RazorDiagnostics,
diagnostic => Assert.Equal("RZ10017", diagnostic.Id));
}
[IntegrationTestFact]
public void BuiltIn_BindToInputText_CanOverrideEvent()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @bind=""@CurrentDate"" @bind:event=""oninput"" @bind:format=""MM/dd"" />
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BuiltIn_BindToInputWithSuffix()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @bind-value=""@CurrentDate"" @bind-value:format=""MM/dd"" />
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BuiltIn_BindToInputWithSuffix_CanOverrideEvent()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<input @bind-value=""@CurrentDate"" @bind-value:event=""oninput"" @bind-value:format=""MM/dd"" />
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BuiltIn_BindToInputWithDefaultFormat()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindInputElement(""custom"", null, ""value"", ""onchange"", isInvariantCulture: false, format: ""MM/dd"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<input type=""custom"" @bind=""@CurrentDate"" />
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BuiltIn_BindToInputWithDefaultFormat_Override()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindInputElement(""custom"", null, ""value"", ""onchange"", isInvariantCulture: false, format: ""MM/dd"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<input type=""custom"" @bind=""@CurrentDate"" @bind:format=""MM/dd/yyyy""/>
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BuiltIn_BindToInputWithDefaultCultureAndDefaultFormat_Override()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
[BindInputElement(""custom"", null, ""value"", ""onchange"", isInvariantCulture: true, format: ""MM/dd"")]
public static class BindAttributes
{
}
}"));
// Act
var generated = CompileToCSharp(@"
<input type=""custom"" @bind=""@CurrentDate"" @bind:format=""MM/dd/yyyy""/>
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region Child Content
[IntegrationTestFact]
public void ChildComponent_WithChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public string MyAttr { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent MyAttr=""abc"">Some text<some-child a='1'>Nested text</some-child></MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var result = CompileToAssembly(generated);
AssertSequencePointsMatchBaseline(result, generated.CodeDocument);
}
[IntegrationTestFact]
public void ChildComponent_WithGenericChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public string MyAttr { get; set; }
[Parameter]
public RenderFragment<string> ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent MyAttr=""abc"">Some text<some-child a='1'>@context.ToLowerInvariant()</some-child></MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var result = CompileToAssembly(generated);
AssertSequencePointsMatchBaseline(result, generated.CodeDocument);
}
[IntegrationTestFact]
public void ChildComponent_WithGenericChildContent_SetsParameterName()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public string MyAttr { get; set; }
[Parameter]
public RenderFragment<string> ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent MyAttr=""abc"">
<ChildContent Context=""item"">
Some text<some-child a='1'>@item.ToLowerInvariant()</some-child>
</ChildContent>
</MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_WithGenericChildContent_SetsParameterNameOnComponent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public string MyAttr { get; set; }
[Parameter]
public RenderFragment<string> ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent MyAttr=""abc"" Context=""item"">
<ChildContent>
Some text<some-child a='1'>@item.ToLowerInvariant()</some-child>
</ChildContent>
</MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_WithElementOnlyChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public RenderFragment ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent><child>hello</child></MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_WithExplicitChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public RenderFragment ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent><ChildContent>hello</ChildContent></MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_WithExplicitGenericChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public RenderFragment<string> ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent><ChildContent>@context</ChildContent></MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void MultipleExplictChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public RenderFragment Header { get; set; }
[Parameter]
public RenderFragment Footer { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent>
<Header>Hi!</Header>
<Footer>@(""bye!"")</Footer>
</MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void BodyAndAttributeChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public RenderFragment<string> Header { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public RenderFragment Footer { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@{ RenderFragment<string> header = (context) => @<div>@context.ToLowerInvariant()</div>; }
<MyComponent Header=@header>
Some Content
</MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var result = CompileToAssembly(generated);
AssertSequencePointsMatchBaseline(result, generated.CodeDocument);
}
[IntegrationTestFact]
public void BodyAndExplicitChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public RenderFragment<string> Header { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public RenderFragment Footer { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@{ RenderFragment<string> header = (context) => @<div>@context.ToLowerInvariant()</div>; }
<MyComponent Header=@header>
<ChildContent>Some Content</ChildContent>
<Footer>Bye!</Footer>
</MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var result = CompileToAssembly(generated);
AssertSequencePointsMatchBaseline(result, generated.CodeDocument);
}
[IntegrationTestFact]
public void MultipleChildContentMatchingComponentName()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public RenderFragment Header { get; set; }
[Parameter]
public RenderFragment Footer { get; set; }
}
public class Header : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent>
<Header>Hi!</Header>
<Footer>Bye!</Footer>
</MyComponent>
<Header>Hello!</Header>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/8460")]
public void VoidTagName()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class Col : ComponentBase
{
[Parameter]
public RenderFragment ChildContent { get; set; }
}
"""));
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components
<Col>in markup</Col>
@{
<Col>in code block</Col>
RenderFragment template = @<Col>in template</Col>;
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, verifyDiagnostics: static diagnostics =>
{
// Malformed C# is generated due to everything after the <Col> tag being considered C#.
Assert.Contains(diagnostics, static d => d.Severity == DiagnosticSeverity.Error);
});
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/8460")]
public void VoidTagName_FullyQualified()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class Col : ComponentBase
{
[Parameter]
public RenderFragment ChildContent { get; set; }
}
"""));
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components
<Test.Col>in markup</Test.Col>
@{
<Test.Col>in code block</Test.Col>
RenderFragment template = @<Test.Col>in template</Test.Col>;
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/8460")]
public void VoidTagName_SelfClosing()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class Col : ComponentBase
{
[Parameter]
public RenderFragment ChildContent { get; set; }
}
"""));
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components
<Col />
@{
<Col />
RenderFragment template = @<Col />;
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/8460")]
public void VoidTagName_NoMatchingComponent()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components
<Col>in markup</Col>
@{
<Col>in code block</Col>
RenderFragment template = @<Col>in template</Col>;
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument, verifyLinePragmas: !DesignTime);
CompileToAssembly(generated, verifyDiagnostics: static diagnostics =>
{
// Malformed C# is generated due to everything after the <Col> tag being considered C#.
Assert.Contains(diagnostics, static d => d.Severity == DiagnosticSeverity.Error);
});
}
#endregion
#region Directives
[IntegrationTestFact]
public void ChildComponent_WithPageDirective()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
@page ""/MyPage""
@page ""/AnotherRoute/{id}""
<MyComponent />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithUsingDirectives()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
namespace Test2
{
public class MyComponent2 : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
@page ""/MyPage""
@page ""/AnotherRoute/{id}""
@using Test2
<MyComponent />
<MyComponent2 />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithUsingDirectives_AmbiguousImport()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
namespace Test2
{
public class SomeComponent : ComponentBase
{
}
}
namespace Test3
{
public class SomeComponent : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Test2
@using Test3
<MyComponent />
<SomeComponent />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var result = CompileToAssembly(generated);
if (DesignTime)
{
Assert.Collection(generated.RazorDiagnostics, d =>
{
Assert.Equal("RZ9985", d.Id);
Assert.Equal(RazorDiagnosticSeverity.Error, d.Severity);
Assert.Equal("Multiple components use the tag 'SomeComponent'. Components: Test2.SomeComponent, Test3.SomeComponent", d.GetMessage(CultureInfo.InvariantCulture));
});
}
}
[IntegrationTestFact]
public void Component_IgnoresStaticAndAliasUsings()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
namespace Test2
{
public class SomeComponent : ComponentBase
{
}
}
namespace Test3
{
public class SomeComponent : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using static Test2.SomeComponent
@using Foo = Test3
<MyComponent />
<SomeComponent /> <!-- Not a component -->");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithMultipleUsingDirectives()
{
var generated = CompileToCSharp(@"
@using System.IO ;@using Microsoft.AspNetCore.Components
; @using System.Reflection;");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildContent_FromAnotherNamespace()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class HeaderComponent : ComponentBase
{
[Parameter]
public RenderFragment Header { get; set; }
}
}
namespace AnotherTest
{
public class FooterComponent : ComponentBase
{
[Parameter]
public RenderFragment<DateTime> Footer { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using AnotherTest
<HeaderComponent>
<Header>Hi!</Header>
</HeaderComponent>
<FooterComponent>
<Footer>@context</Footer>
</FooterComponent>
<Test.HeaderComponent>
<Header>Hi!</Header>
</Test.HeaderComponent>
<AnotherTest.FooterComponent>
<Footer>@context</Footer>
</AnotherTest.FooterComponent>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithNamespaceDirective()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class HeaderComponent : ComponentBase
{
[Parameter]
public string Header { get; set; }
}
}
namespace AnotherTest
{
public class FooterComponent : ComponentBase
{
[Parameter]
public string Footer { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Test
@namespace AnotherTest
<HeaderComponent Header='head'>
</HeaderComponent>
<FooterComponent Footer='feet'>
</FooterComponent>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithNamespaceDirective_WithWhitespace()
{
var generated = CompileToCSharp(@"
@namespace My.Custom.Namespace
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithPreserveWhitespaceDirective_True()
{
// Arrange / Act
var generated = CompileToCSharp(@"
@preservewhitespace true
<ul>
@foreach (var item in Enumerable.Range(1, 100))
{
<li>
@item
</li>
}
</ul>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithPreserveWhitespaceDirective_False()
{
// Arrange / Act
var generated = CompileToCSharp(@"
@preservewhitespace false
<ul>
@foreach (var item in Enumerable.Range(1, 100))
{
<li>
@item
</li>
}
</ul>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithPreserveWhitespaceDirective_Invalid()
{
// Arrange / Act
var generated = CompileToCSharp(@"
@preservewhitespace someVariable
@code {
bool someVariable = false;
}
");
// Assert
Assert.Collection(generated.RazorDiagnostics, d => { Assert.Equal("RZ1038", d.Id); });
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10963")]
public void InheritsDirective()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
namespace Test;
public class BaseComponent : Microsoft.AspNetCore.Components.ComponentBase
{
}
"""));
// Act
var generated = CompileToCSharp("""
@inherits BaseComponent
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/7169")]
public void InheritsDirective_NullableReferenceType()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
namespace Test;
public class BaseComponent<T> : Microsoft.AspNetCore.Components.ComponentBase
{
protected T _field = default!;
}
"""));
// Act
var generated = CompileToCSharp("""
@inherits BaseComponent<string?>
<h1>My component</h1>
@(_field.ToString())
""",
nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, DesignTime
? // x:\dir\subdir\Test\TestComponent.cshtml(4,7): warning CS8602: Dereference of a possibly null reference.
// __o = _field.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "_field").WithLocation(4, 7)
: // x:\dir\subdir\Test\TestComponent.cshtml(4,7): warning CS8602: Dereference of a possibly null reference.
// __o = _field.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "_field").WithLocation(4, 3));
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/7169")]
public void InheritsDirective_NullableReferenceType_NullableDisabled()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
namespace Test;
public class BaseComponent<T> : Microsoft.AspNetCore.Components.ComponentBase
{
protected T _field;
}
"""));
// Act
var generated = CompileToCSharp("""
@inherits BaseComponent<string?>
<h1>My component</h1>
@(_field.ToString())
""",
nullableEnable: false,
expectedCSharpDiagnostics:
// (1,31): warning CS8669: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.
// public partial class TestComponent : BaseComponent<string?>
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotationInGeneratedCode, "?").WithLocation(1, 31));
// Assert
Assert.Empty(generated.RazorDiagnostics);
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, DesignTime
? [
// x:\dir\subdir\Test\TestComponent.cshtml(1,21): warning CS8669: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.
// BaseComponent<string?> __typeHelper = default!;
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotationInGeneratedCode, "?").WithLocation(1, 21),
// (14,62): warning CS8669: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.
// public partial class TestComponent : BaseComponent<string?>
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotationInGeneratedCode, "?").WithLocation(14, 62)
]
: [
// (1,31): warning CS8669: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.
// public partial class TestComponent : BaseComponent<string?>
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotationInGeneratedCode, "?").WithLocation(1, 31)
]);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10863")]
public void PageDirective_NoForwardSlash()
{
// Act
var generated = CompileToCSharp("""
@page "MyPage"
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10863")]
public void PageDirective_NoForwardSlash_WithComment()
{
// Act
var generated = CompileToCSharp("""
@page /* comment */ "MyPage"
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10863")]
public void PageDirective_MissingRoute()
{
// Act
var generated = CompileToCSharp("""
@page
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
// Design time writer doesn't correctly emit pragmas for missing tokens, so don't validate them in design time
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument, verifyLinePragmas: !DesignTime);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10863")]
public void PageDirective_MissingRoute_WithComment()
{
// Act
var generated = CompileToCSharp("""
@page /* comment */
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
// Design time writer doesn't correctly emit pragmas for missing tokens, so don't validate them in design time
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument, verifyLinePragmas: !DesignTime);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10863")]
public void UsingDirective()
{
// Act
var generated = CompileToCSharp("""
@using System.Collections
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region EventCallback
[IntegrationTestFact]
public void EventCallback_CanPassEventCallback_Explicitly()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public EventCallback OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent OnClick=""@(EventCallback.Factory.Create(this, Increment))""/>
@code {
private int counter;
private void Increment() {
counter++;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallbackOfT_GenericComponent_ExplicitType()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<T> : ComponentBase
{
[Parameter]
public EventCallback<T> OnClick { get; set; }
}
public class MyType
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent T=""MyType"" OnClick=""@((MyType arg) => counter++)""/>
@code {
private int counter;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallbackOfT_GenericComponent_ExplicitType_MethodGroup()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<T> : ComponentBase
{
[Parameter]
public EventCallback<T> OnClick { get; set; }
}
public class MyType
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent T=""MyType"" OnClick=""Increment""/>
@code {
private int counter;
public void Increment(MyType type) => counter++;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallback_CanPassEventCallbackOfT_Explicitly()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<MyComponent OnClick=""@(EventCallback.Factory.Create<MouseEventArgs>(this, Increment))""/>
@code {
private int counter;
private void Increment() {
counter++;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallback_CanPassEventCallback_Implicitly_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public EventCallback OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent OnClick=""@Increment""/>
@code {
private int counter;
private void Increment() {
counter++;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallback_CanPassEventCallback_Implicitly_ActionOfObject()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public EventCallback OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent OnClick=""@Increment""/>
@code {
private int counter;
private void Increment(object e) {
counter++;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallback_CanPassEventCallback_Implicitly_FuncOfTask()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public EventCallback OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent OnClick=""@Increment""/>
@code {
private int counter;
private Task Increment() {
counter++;
return Task.CompletedTask;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallback_CanPassEventCallback_Implicitly_FuncOfobjectTask()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public EventCallback OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent OnClick=""@Increment""/>
@code {
private int counter;
private Task Increment(object e) {
counter++;
return Task.CompletedTask;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallback_CanPassEventCallbackOfT_Implicitly_Action()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent OnClick=""@Increment""/>
@code {
private int counter;
private void Increment() {
counter++;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallback_CanPassEventCallbackOfT_Implicitly_ActionOfT()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<MyComponent OnClick=""@Increment""/>
@code {
private int counter;
private void Increment(MouseEventArgs e) {
counter++;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallback_CanPassEventCallbackOfT_Implicitly_FuncOfTask()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent OnClick=""@Increment""/>
@code {
private int counter;
private Task Increment() {
counter++;
return Task.CompletedTask;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallback_CanPassEventCallbackOfT_Implicitly_FuncOfTTask()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<MyComponent OnClick=""@Increment""/>
@code {
private int counter;
private Task Increment(MouseEventArgs e) {
counter++;
return Task.CompletedTask;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventCallback_CanPassEventCallbackOfT_Implicitly_TypeMismatch()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<MyComponent OnClick=""@Increment""/>
@code {
private int counter;
private void Increment(ChangeEventArgs e) {
counter++;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(2,24): error CS1503: Argument 2: cannot convert from 'method group' to 'Microsoft.AspNetCore.Components.EventCallback'
// Increment
Diagnostic(ErrorCode.ERR_BadArgType, "Increment").WithArguments("2", "method group", "Microsoft.AspNetCore.Components.EventCallback").WithLocation(2, 24));
}
[IntegrationTestFact]
public void EventCallbackOfT_GenericComponent_MissingTypeParameterBinding_01()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<T, T2> : ComponentBase
{
[Parameter]
public EventCallback<T> OnClick { get; set; }
}
public class MyType
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent OnClick=""@((MyType arg) => counter++)""/>
@code {
private int counter;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument, verifyLinePragmas: DesignTime);
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(4,17): warning CS0169: The field 'TestComponent.counter' is never used
// private int counter;
Diagnostic(ErrorCode.WRN_UnreferencedField, "counter").WithArguments("Test.TestComponent.counter").WithLocation(4, 17));
}
[IntegrationTestFact]
public void EventCallbackOfT_GenericComponent_MissingTypeParameterBinding_02()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<T, T2> : ComponentBase
{
[Parameter]
public EventCallback<T> OnClick1 { get; set; }
[Parameter]
public EventCallback<T2> OnClick2 { get; set; }
}
public class MyType
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent OnClick=""@((MyType arg) => counter++)""/>
@code {
private int counter;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument, verifyLinePragmas: DesignTime);
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(4,17): warning CS0169: The field 'TestComponent.counter' is never used
// private int counter;
Diagnostic(ErrorCode.WRN_UnreferencedField, "counter").WithArguments("Test.TestComponent.counter").WithLocation(4, 17));
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/aspnetcore/issues/48526")]
public void EventCallbackOfT_Array()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components.Forms;
namespace Test;
public class MyComponent<TItem> : InputBase<TItem[]>
{
protected override bool TryParseValueFromString(string value, out TItem[] result, out string validationErrorMessage) => throw null;
}
"""));
// Act
var generated = CompileToCSharp("""
<MyComponent @bind-Value="Selected" />
@code {
string[] Selected { get; set; } = Array.Empty<string>();
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region Event Handlers
[IntegrationTestFact]
public void Component_WithImplicitLambdaEventHandler()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @onclick=""() => Increment()""/>
@code {
private int counter;
private void Increment() {
counter++;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_WithLambdaEventHandler()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public Action<EventArgs> OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent OnClick=""@(e => { Increment(); })""/>
@code {
private int counter;
private void Increment() {
counter++;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
// Regression test for #954 - we need to allow arbitrary event handler
// attributes with weak typing.
[IntegrationTestFact]
public void ChildComponent_WithWeaklyTypeEventHandler()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class DynamicElement : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<DynamicElement @onclick=""OnClick"" />
@code {
private Action<MouseEventArgs> OnClick { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_WithExplicitEventHandler()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public Action<EventArgs> OnClick { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent OnClick=""@Increment""/>
@code {
private int counter;
private void Increment(EventArgs e) {
counter++;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_OnElement_WithString()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input onclick=""foo"" />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_OnElement_WithNoArgsLambdaDelegate()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @onclick=""() => { }"" />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_OnElement_WithEventArgsLambdaDelegate()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @onclick=""x => { }"" />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_OnElement_WithNoArgMethodGroup()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @onclick=""OnClick"" />
@code {
void OnClick() {
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_OnElement_WithoutCloseTag()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<div>
<input @onclick=""OnClick"">
</div>
@code {
void OnClick() {
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_OnElement_WithEventArgsMethodGroup()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @onclick=""OnClick"" />
@code {
void OnClick(MouseEventArgs e) {
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_OnElement_ArbitraryEventName_WithEventArgsMethodGroup()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @onclick=""OnClick"" />
@code {
void OnClick(EventArgs e) {
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void AsyncEventHandler_OnElement_Action_MethodGroup()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using System.Threading.Tasks
@using Microsoft.AspNetCore.Components.Web
<input @onclick=""OnClick"" />
@code {
Task OnClick()
{
return Task.CompletedTask;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void AsyncEventHandler_OnElement_ActionEventArgs_MethodGroup()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using System.Threading.Tasks
@using Microsoft.AspNetCore.Components.Web
<input @onclick=""OnClick"" />
@code {
Task OnClick(MouseEventArgs e)
{
return Task.CompletedTask;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void AsyncEventHandler_OnElement_Action_Lambda()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using System.Threading.Tasks
@using Microsoft.AspNetCore.Components.Web
<input @onclick=""@(async () => await Task.Delay(10))"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void AsyncEventHandler_OnElement_ActionEventArgs_Lambda()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using System.Threading.Tasks
@using Microsoft.AspNetCore.Components.Web
<input @onclick=""@(async (e) => await Task.Delay(10))"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_OnElement_WithLambdaDelegate()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @onclick=""x => { }"" />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_OnElement_WithDelegate()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @onclick=""OnClick"" />
@code {
void OnClick(MouseEventArgs e) {
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_AttributeNameIsCaseSensitive()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @onCLICK=""OnClick"" />
@code {
void OnClick(MouseEventArgs e) {
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_PreventDefault_StopPropagation_Minimized()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<button @onclick:preventDefault @onclick:stopPropagation>Click Me</button>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_PreventDefault_StopPropagation()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<button @onclick=""() => Foo = false"" @onfocus:preventDefault=""true"" @onclick:stopPropagation=""Foo"" @onfocus:stopPropagation=""false"">Click Me</button>
@code {
bool Foo { get; set; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_WithDelegate_PreventDefault()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @onfocus=""OnFocus"" @onfocus:preventDefault=""ShouldPreventDefault()"" />
@code {
void OnFocus(FocusEventArgs e) { }
bool ShouldPreventDefault() { return false; }
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandler_PreventDefault_Duplicates()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<input @onclick:preventDefault=""true"" @onclick:preventDefault />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region Generics
[IntegrationTestFact]
public void ChildComponent_Generic()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TItem=string Item=""@(""hi"")""/>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/8467")]
public void ChildComponent_AtSpecifiedInRazorFileForTypeParameter()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test
{
public class C<T> : ComponentBase
{
[Parameter] public int Item { get; set; }
}
}
"""));
var generated = CompileToCSharp("""<C T="@string" Item="1" />""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_NonPrimitiveType()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
}
public class CustomType
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TItem=""CustomType"" Item=""new CustomType()""/>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_NonPrimitiveTypeRenderFragment()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public RenderFragment<CustomType> ChildContent { get; set; }
}
public class CustomType
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Item=""new CustomType()"">@context.ToString()</MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_Generic_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Item=""@(""hi"")""/>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_Generic_TypeInference_Multiple()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Item=""@(""hi"")""/>
<MyComponent Item=""@(""how are you?"")""/>
<MyComponent Item=""@(""bye!"")""/>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_Explicit()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public System.Collections.Generic.IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Column<TItem> : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid TItem=""DateTime"" Items=""@(Array.Empty<DateTime>())""><Column /><Column /></Grid>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_ExplicitOverride()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public System.Collections.Generic.IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Column<TItem> : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid TItem=""DateTime"" Items=""@(Array.Empty<DateTime>())""><Column TItem=""System.TimeZoneInfo"" /></Grid>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_NotCascaded_Explicit()
{
// The point of this test is to show that, without [CascadingTypeParameter], we don't cascade
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class Grid<TItem> : ComponentBase
{
[Parameter] public System.Collections.Generic.IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Column<TItem> : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid TItem=""DateTime"" Items=""@(Array.Empty<DateTime>())""><Column /></Grid>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.GenericComponentTypeInferenceUnderspecified.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void CascadingGenericInference_NotCascaded_Inferred()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public System.Collections.Generic.IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Column<TItem> : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid Items=""@(Array.Empty<DateTime>())""><Column /><Column /></Grid>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_Inferred_WithConstraints()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public RenderFragment ColumnsTemplate { get; set; }
}
public abstract partial class BaseColumn<TItem> : ComponentBase where TItem : class
{
[CascadingParameter]
internal Grid<TItem> Grid { get; set; }
}
public class Column<TItem> : BaseColumn<TItem>, IGridFieldColumn<TItem> where TItem : class
{
[Parameter]
public string FieldName { get; set; }
}
internal interface IGridFieldColumn<TItem> where TItem : class
{
}
public class WeatherForecast { }
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid TItem=""WeatherForecast"" Items=""@(Array.Empty<WeatherForecast>())"">
<ColumnsTemplate>
<Column Title=""Date"" FieldName=""Date"" Format=""d"" Width=""10rem"" />
</ColumnsTemplate>
</Grid>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_Inferred_MultipleConstraints()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public RenderFragment ColumnsTemplate { get; set; }
}
public abstract partial class BaseColumn<TItem> : ComponentBase where TItem : class, new()
{
[CascadingParameter]
internal Grid<TItem> Grid { get; set; }
}
public class Column<TItem> : BaseColumn<TItem>, IGridFieldColumn<TItem> where TItem : class, new()
{
[Parameter]
public string FieldName { get; set; }
}
internal interface IGridFieldColumn<TItem> where TItem : class
{
}
public class WeatherForecast { }
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid TItem=""WeatherForecast"" Items=""@(Array.Empty<WeatherForecast>())"">
<ColumnsTemplate>
<Column Title=""Date"" FieldName=""Date"" Format=""d"" Width=""10rem"" />
</ColumnsTemplate>
</Grid>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_Inferred_MultipleConstraints_ClassesAndInterfaces()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
using Models;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public RenderFragment ColumnsTemplate { get; set; }
}
public abstract partial class BaseColumn<TItem> : ComponentBase where TItem : WeatherForecast, new()
{
[CascadingParameter]
internal Grid<TItem> Grid { get; set; }
}
public class Column<TItem> : BaseColumn<TItem>, IGridFieldColumn<TItem> where TItem : WeatherForecast, new()
{
[Parameter]
public string FieldName { get; set; }
}
internal interface IGridFieldColumn<TItem> where TItem : class
{
}
}
namespace Models {
public class WeatherForecast { }
}"));
// Act
var generated = CompileToCSharp(@"
@using Models;
<Grid TItem=""WeatherForecast"" Items=""@(Array.Empty<WeatherForecast>())"">
<ColumnsTemplate>
<Column Title=""Date"" FieldName=""Date"" Format=""d"" Width=""10rem"" />
</ColumnsTemplate>
</Grid>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_Inferred_MultipleConstraints_GenericClassConstraints()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public RenderFragment ColumnsTemplate { get; set; }
}
public abstract partial class BaseColumn<TItem> : ComponentBase where TItem : System.Collections.Generic.IEnumerable<TItem>
{
[CascadingParameter]
internal Grid<TItem> Grid { get; set; }
}
public class Column<TItem> : BaseColumn<TItem> where TItem : System.Collections.Generic.IEnumerable<TItem>
{
[Parameter]
public string FieldName { get; set; }
}
}
namespace Models {
using System;
using System.Collections;
using System.Collections.Generic;
public class WeatherForecast : IEnumerable<WeatherForecast> {
public IEnumerator<WeatherForecast> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
}"));
// Act
var generated = CompileToCSharp(@"
@using Models;
<Grid TItem=""WeatherForecast"" Items=""@(Array.Empty<WeatherForecast>())"">
<ColumnsTemplate>
<Column Title=""Date"" FieldName=""Date"" Format=""d"" Width=""10rem"" />
</ColumnsTemplate>
</Grid>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_Partial_CreatesError()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public System.Collections.Generic.IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Column<TItem, TChildOther> : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid Items=""@(Array.Empty<DateTime>())""><Column TChildOther=""long"" /></Grid>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.GenericComponentMissingTypeArgument.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void CascadingGenericInference_WithSplatAndKey()
{
// This is an integration test to show that our type inference code doesn't
// have bad interactions with some of the other more complicated transformations
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public System.Collections.Generic.IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Column<TItem> : ComponentBase
{
[Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object> OtherAttributes { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@{ var parentKey = new object(); var childKey = new object(); }
<Grid @key=""@parentKey"" Items=""@(Array.Empty<DateTime>())"">
<Column @key=""@childKey"" Title=""Hello"" Another=""@DateTime.MinValue"" />
</Grid>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_Multilayer()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Ancestor<TItem> : ComponentBase
{
[Parameter] public System.Collections.Generic.IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Passthrough : ComponentBase
{
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Child<TItem> : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Ancestor Items=""@(Array.Empty<DateTime>())""><Passthrough><Child /></Passthrough></Ancestor>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_Override_Multilayer()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class TreeNode<TItem> : ComponentBase
{
[Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] public TItem Item { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<TreeNode Item=""@DateTime.Now"">
<TreeNode Item=""@System.Threading.Thread.CurrentThread"">
<TreeNode>
<TreeNode />
</TreeNode>
</TreeNode>
<TreeNode />
</TreeNode>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_Override()
{
// This test is to show that, even if an ancestor is trying to cascade its generic types,
// a descendant can still override that through inference
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public System.Collections.Generic.IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Column<TItem> : ComponentBase
{
[Parameter] public TItem OverrideParam { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid Items=""@(Array.Empty<DateTime>())""><Column OverrideParam=""@(""Some string"")"" /></Grid>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_NotCascaded_CreatesError()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class Grid<TItem> : ComponentBase
{
[Parameter] public System.Collections.Generic.IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Column<TItem> : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid Items=""@(Array.Empty<DateTime>())""><Column /></Grid>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.GenericComponentTypeInferenceUnderspecified.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void CascadingGenericInference_GenericChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public System.Collections.Generic.IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Column<TItem> : ComponentBase
{
[Parameter] public RenderFragment<TItem> ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid Items=""@(Array.Empty<DateTime>())""><Column>@context.Year</Column></Grid>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_GenericLambda()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
public class Grid<TItem> : ComponentBase
{
[Parameter] public System.Collections.Generic.IEnumerable<TItem> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Column<TItem, TOutput> : ComponentBase
{
[Parameter] public System.Func<TItem, TOutput> SomeLambda { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid Items=""@(Array.Empty<DateTime>())""><Column SomeLambda=""@(x => x.Year)"" /></Grid>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_MultipleTypes()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TKey))]
[CascadingTypeParameter(nameof(TValue))]
[CascadingTypeParameter(nameof(TOther))]
public class Parent<TKey, TValue, TOther> : ComponentBase
{
[Parameter] public Dictionary<TKey, TValue> Data { get; set; }
[Parameter] public TOther Other { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Child<TOther, TValue, TKey, TChildOnly> : ComponentBase
{
[Parameter] public ICollection<TChildOnly> ChildOnlyItems { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Parent Data=""@(new System.Collections.Generic.Dictionary<int, string>())"" Other=""@DateTime.MinValue"">
<Child ChildOnlyItems=""@(new[] { 'a', 'b', 'c' })"" />
</Parent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_WithUnrelatedType_CreatesError()
{
// It would succeed if you changed this to Column<TItem, TUnrelated>, or if the Grid took a parameter
// whose type included TItem and not TUnrelated. It just doesn't work if the only inference parameters
// also include unrelated generic types, because the inference methods we generate don't know what
// to do with extra type parameters. It would be nice just to ignore them, but at the very least we
// have to rewrite their names to avoid clashes and figure out whether multiple unrelated generic
// types with the same name should be rewritten to the same name or unique names.
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TItem))]
[CascadingTypeParameter(nameof(TUnrelated))]
public class Grid<TItem, TUnrelated> : ComponentBase
{
[Parameter] public System.Collections.Generic.Dictionary<TItem, TUnrelated> Items { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Column<TItem> : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Grid Items=""@(new Dictionary<int, string>())""><Column /></Grid>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.GenericComponentTypeInferenceUnderspecified.Id, diagnostic.Id);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9631")]
public void CascadingGenericInference_GenericArgumentNested()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
namespace Test;
[CascadingTypeParameter(nameof(T))]
public class Grid<T> : ComponentBase
{
[Parameter] public Func<List<T>>? Data { get; set; }
}
public partial class GridColumn<T> : ComponentBase
{
}
"""));
// Act
var generated = CompileToCSharp("""
<Grid Data="@(() => new List<string>())">
<GridColumn />
</Grid>
""", nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9631")]
public void CascadingGenericInference_GenericArgumentNested_Dictionary()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
namespace Test
{
[CascadingTypeParameter(nameof(T))]
public class Grid<T> : ComponentBase
{
[Parameter] public Func<Dictionary<X, T>>? Data { get; set; }
}
public partial class GridColumn<T> : ComponentBase
{
}
}
public class X { }
"""));
// Act
var generated = CompileToCSharp("""
<Grid Data="@(() => new Dictionary<X, string>())">
<GridColumn />
</Grid>
""", nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9631")]
public void CascadingGenericInference_GenericArgumentNested_Dictionary_02()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
namespace Test;
[CascadingTypeParameter(nameof(T))]
public class Grid<T> : ComponentBase
{
[Parameter] public Func<Dictionary<X, T>>? Data { get; set; }
}
public partial class GridColumn<T> : ComponentBase
{
}
public class X { }
"""));
// Act
var generated = CompileToCSharp("""
<Grid Data="@(() => new Dictionary<X, string>())">
<GridColumn />
</Grid>
""", nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9631")]
public void CascadingGenericInference_GenericArgumentNested_Dictionary_03()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
namespace Test
{
[CascadingTypeParameter(nameof(T))]
public class Grid<T> : ComponentBase
{
[Parameter] public Dictionary<X, T>? Data { get; set; }
}
public partial class GridColumn<T> : ComponentBase
{
}
}
public class X { }
"""));
// Act
var generated = CompileToCSharp("""
<Grid Data="@(new Dictionary<X, string>())">
<GridColumn />
</Grid>
""", nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9631")]
public void CascadingGenericInference_GenericArgumentNested_Dictionary_Dynamic()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
namespace Test
{
[CascadingTypeParameter(nameof(T))]
public class Grid<T> : ComponentBase
{
[Parameter] public Dictionary<dynamic, T>? Data { get; set; }
}
public partial class GridColumn<T> : ComponentBase
{
}
}
"""));
// Act
var generated = CompileToCSharp("""
<Grid Data="@(new Dictionary<dynamic, string>())">
<GridColumn />
</Grid>
""", nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_CombiningMultipleAncestors()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(TOne))]
public class ParentOne<TOne> : ComponentBase
{
[Parameter] public TOne Value { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
[CascadingTypeParameter(nameof(TTwo))]
public class ParentTwo<TTwo> : ComponentBase
{
[Parameter] public TTwo Value { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
}
public class Child<TOne, TTwo> : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<ParentOne Value=""@int.MaxValue"">
<ParentTwo Value=""@(""Hello"")"">
<Child />
</ParentTwo>
</ParentOne>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/7103")]
public void CascadingGenericInference_ParameterInNamespace()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace MyApp
{
public class MyClass<T> { }
}
namespace MyApp.Components
{
[CascadingTypeParameter(nameof(T))]
public class ParentComponent<T> : ComponentBase
{
[Parameter] public MyApp.MyClass<T> Parameter { get; set; } = null!;
}
public class ChildComponent<T> : ComponentBase { }
}
"""));
// Act
var generated = CompileToCSharp("""
@namespace MyApp.Components
<ParentComponent Parameter="new MyClass<string>()">
<ChildComponent />
</ParentComponent>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CascadingGenericInference_Tuple()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test
{
[CascadingTypeParameter(nameof(T))]
public class ParentComponent<T> : ComponentBase
{
[Parameter] public (T, T) Parameter { get; set; }
}
public class ChildComponent<T> : ComponentBase { }
}
"""));
// Act
var generated = CompileToCSharp("""
<ParentComponent Parameter="(1, 2)">
<ChildComponent />
</ParentComponent>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/7428")]
public void CascadingGenericInference_NullableEnabled()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
[CascadingTypeParameter(nameof(TRow))]
public class Parent<TRow>: ComponentBase
{
[Parameter]
public RenderFragment<TRow>? ChildContent { get; set; }
}
public class Child<TRow> : ComponentBase
{
[Parameter]
public RenderFragment<TRow>? ChildContent { get; set; }
}
"""));
// Act
var generated = CompileToCSharp("""
<Parent TRow="string">
<Child Context="childContext">@childContext.Length</Child>
</Parent>
""", nullableEnable: true);
// Assert
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_GenericWeaklyTypedAttribute()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TItem=string Item=""@(""hi"")"" Other=""@(17)""/>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_GenericWeaklyTypedAttribute_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Item=""@(""hi"")"" Other=""@(17)""/>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_GenericBind()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter]
public TItem Item { get; set; }
[Parameter]
public Action<TItem> ItemChanged { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TItem=string @bind-Item=Value/>
@code {
string Value;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_GenericBind_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter]
public TItem Item { get; set; }
[Parameter]
public Action<TItem> ItemChanged { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Item=Value/>
<MyComponent @bind-Item=Value/>
@code {
string Value;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_GenericBindWeaklyTyped()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TItem=string @bind-Item=Value/>
@code {
string Value;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_GenericBindWeaklyTyped_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Value { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Item=Value Value=@(18)/>
@code {
string Value;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_GenericChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public RenderFragment<TItem> ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TItem=string Item=""@(""hi"")"">
<div>@context.ToLower()</div>
</MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_GenericChildContent_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public RenderFragment<TItem> ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Item=""@(""hi"")"">
<div>@context.ToLower()</div>
</MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_NonGenericParameterizedChildContent_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public RenderFragment<TItem> GenericFragment { get; set; }
[Parameter] public RenderFragment<int> IntFragment { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Item=""@(""hi"")"">
<GenericFragment>@context.ToLower()</GenericFragment>
<IntFragment>@context</IntFragment>
</MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_WithFullyQualifiedTagName()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public RenderFragment<TItem> ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Test.MyComponent Item=""@(""hi"")"">
<div>@context.ToLower()</div>
</Test.MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_MultipleGenerics()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem1, TItem2> : ComponentBase
{
[Parameter] public TItem1 Item { get; set; }
[Parameter] public RenderFragment<TItem1> ChildContent { get; set; }
[Parameter] public RenderFragment<Context> AnotherChildContent { get; set; }
public class Context
{
public TItem2 Item { get; set; }
}
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TItem1=string TItem2=int Item=""@(""hi"")"">
<ChildContent><div>@context.ToLower()</div></ChildContent>
<AnotherChildContent Context=""item"">
@System.Math.Max(0, item.Item);
</AnotherChildContent>
</MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ChildComponent_MultipleGenerics_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem1, TItem2> : ComponentBase
{
[Parameter] public TItem1 Item { get; set; }
[Parameter] public List<TItem2> Items { get; set; }
[Parameter] public RenderFragment<TItem1> ChildContent { get; set; }
[Parameter] public RenderFragment<Context> AnotherChildContent { get; set; }
public class Context
{
public TItem2 Item { get; set; }
}
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Item=""@(""hi"")"" Items=@(new List<long>())>
<ChildContent><div>@context.ToLower()</div></ChildContent>
<AnotherChildContent Context=""item"">
@System.Math.Max(0, item.Item);
</AnotherChildContent>
</MyComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void NonGenericComponent_WithGenericEventHandler()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyEventArgs { }
public class MyComponent : ComponentBase
{
[Parameter] public string Item { get; set; }
[Parameter] public EventCallback<MyEventArgs> Event { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Item=""Hello"" MyEvent=""MyEventHandler"" />
@code {
public void MyEventHandler() {}
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_WithKey()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TItem=int Item=""3"" @key=""_someKey"" />
@code {
private object _someKey = new object();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_WithKey_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Item=""3"" @key=""_someKey"" />
@code {
private object _someKey = new object();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_WithComponentRef_CreatesDiagnostic()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent TItem=int Item=""3"" @ref=""_my"" />
@code {
private MyComponent<int> _my;
public void Foo() { System.GC.KeepAlive(_my); }
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_WithComponentRef_TypeInference_CreatesDiagnostic()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Item=""3"" @ref=""_my"" />
@code {
private MyComponent<int> _my;
public void Foo() { System.GC.KeepAlive(_my); }
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_NonGenericParameter_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
using Test.Shared;
namespace Test
{
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public MyClass Foo { get; set; }
}
}
namespace Test.Shared
{
public class MyClass
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Test.Shared
<MyComponent Item=""3"" Foo=""@Hello"" />
@code {
MyClass Hello = new MyClass();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_NonGenericEventCallback_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyEventArgs { }
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public EventCallback MyEvent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Test
<MyComponent Item=""3"" MyEvent=""x => {}"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_GenericEventCallback_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyEventArgs { }
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public EventCallback<MyEventArgs> MyEvent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Test
<MyComponent Item=""3"" MyEvent=""x => {}"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_NestedGenericEventCallback_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyEventArgs { }
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public EventCallback<List<Dictionary<string, MyEventArgs[]>>> MyEvent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Test
<MyComponent Item=""3"" MyEvent=""x => {}"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_GenericEventCallbackWithGenericTypeParameter_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyEventArgs { }
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public EventCallback<TItem> MyEvent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Test
<MyComponent Item=""3"" MyEvent=""(int x) => {}"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_GenericEventCallbackWithGenericTypeParameter_NestedTypeExplicit()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyEventArgs { }
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public EventCallback<TItem> MyEvent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@typeparam TChild
@using Test
<MyComponent TItem=""TChild"" MyEvent=""(TChild x) => {}"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_GenericEventCallbackWithGenericTypeParameter_NestedTypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyEventArgs { }
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public EventCallback<TItem> MyEvent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@typeparam TChild
@using Test
<MyComponent Item=""ChildItem"" MyEvent=""MyChildEvent"" />
@code
{
[Parameter] public TChild ChildItem { get; set; }
[Parameter] public EventCallback<TChild> MyChildEvent { get; set; }
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_GenericEventCallbackWithNestedGenericTypeParameter_TypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
namespace Test
{
public class MyEventArgs { }
public class MyComponent<TItem> : ComponentBase
{
[Parameter] public TItem Item { get; set; }
[Parameter] public EventCallback<IEnumerable<TItem>> MyEvent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@using Test
@using System.Collections.Generic
<MyComponent Item=""3"" MyEvent=""(IEnumerable<int> x) => {}"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestTheory, WorkItem("https://github.com/dotnet/razor/issues/7074")]
[InlineData("struct", null, "1")]
[InlineData("class", null, "string.Empty")]
[InlineData("notnull", null, "1")]
[InlineData("unmanaged", null, "1")]
[InlineData(null, "new()", "1")]
public void GenericComponent_ConstraintOrdering(string first, string last, string arg)
{
// Arrange
if (first != null)
{
first = $"{first}, ";
}
if (last != null)
{
last = $", {last}";
}
AdditionalSyntaxTrees.Add(Parse($$"""
using Microsoft.AspNetCore.Components;
using System;
namespace Test;
public class MyComponent<T> : ComponentBase where T : {{first}}IComparable{{last}}
{
[Parameter] public T Parameter { get; set; }
}
"""));
// Act
var generated = CompileToCSharp($"""
@using Test
<MyComponent Parameter="{arg}" />
""");
// Assert
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_UnmanagedConstraint()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyComponent<T> : ComponentBase where T : unmanaged
{
[Parameter] public T Parameter { get; set; }
}
"""));
// Act
var generated = CompileToCSharp("""
@using Test
<MyComponent Parameter="1" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9592")]
public void GenericComponent_TypeParameterOrdering()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public interface IInterfaceConstraint<T> { }
public interface IComposedInterface : IInterfaceConstraint<string> { }
public class MyComponent<TService, TKey> : ComponentBase
where TService : IInterfaceConstraint<TKey>
{
[Parameter] public TKey? Value { get; set; }
[Parameter] public EventCallback<TKey> ValueChanged { get; set; }
}
"""));
// Act
var generated = CompileToCSharp("""
<MyComponent TKey="string" TService="IComposedInterface" @bind-Value="_componentValue" />
<MyComponent TService="IComposedInterface" TKey="string" @bind-Value="_componentValue" />
@code {
string _componentValue = string.Empty;
}
""",
nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void GenericComponent_MissingTypeParameter_SystemInNamespace()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyComponent<TItem> : ComponentBase;
"""));
// Act
var generated = CompileToCSharp("""
@namespace Test.System
<MyComponent />
""");
// Assert
CompileToAssembly(generated);
generated.RazorDiagnostics.Verify(
// x:\dir\subdir\Test\TestComponent.cshtml(2,1): error RZ10001: The type of component 'MyComponent' cannot be inferred based on the values provided. Consider specifying the type arguments directly using the following attributes: 'TItem'.
Diagnostic("RZ10001").WithLocation(2, 1));
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10827")]
public void GenericTypeCheck()
{
var generated = CompileToCSharp("""
<TestComponent Data="null" />
@code {
private class System
{
private class String
{
}
}
[Parameter]
public List<global::System.String> Data { get; set; }
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11505")]
public void CaptureParametersConstraint()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public interface IMyInterface;
public class MyClass<T> where T : IMyInterface;
[CascadingTypeParameter(nameof(T))]
public class MyComponent<T> : ComponentBase where T : IMyInterface
{
[Parameter] public MyClass<T> Param { get; set; }
}
"""));
var generated = CompileToCSharp("""
<MyComponent Param="new MyClass<IMyInterface>()" />
""");
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11552")]
public void GenericComponentTypeUsage()
{
// Act
var generated = CompileToCSharp("""
@typeparam TItem
@code {
[Parameter]
public TItem MyItem { get; set; }
}
<TestComponent TItem="string" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11552")]
public void GenericComponentTypeUsageWithInference()
{
// Act
var generated = CompileToCSharp("""
@typeparam TItem
@code {
[Parameter]
public TItem MyItem { get; set; }
}
<TestComponent MyItem="1" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11552")]
public void GenericComponentMultipleTypeParamUsage()
{
// Act
var generated = CompileToCSharp("""
@typeparam TItem
@typeparam TItem2
@code {
[Parameter]
public TItem MyItem { get; set; }
[Parameter]
public TItem2 MyItem2 { get; set; }
}
<TestComponent TItem2="int" TItem="string" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11552")]
public void GenericComponentTypeParamUsageWithImplicitExpression()
{
// Act
var generated = CompileToCSharp("""
@typeparam TItem
@code {
[Parameter]
public TItem MyItem { get; set; }
}
<TestComponent TItem="@string" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11552")]
public void GenericComponentTypeParamUsageWithImplicitExpression2()
{
// Act
var generated = CompileToCSharp("""
@typeparam TItem
@code {
[Parameter]
public TItem MyItem { get; set; }
}
<TestComponent TItem="@(string)" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11552")]
public void GenericComponentTypeUsageWhitespace()
{
// Act
var generated = CompileToCSharp("""
@typeparam TItem
@code {
[Parameter]
public TItem MyItem { get; set; }
}
<TestComponent TItem=" string " />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11552")]
public void GenericComponentTypeUsageWithGenericType()
{
// Act
var generated = CompileToCSharp("""
@typeparam TItem
@code {
[Parameter]
public TItem MyItem { get; set; }
}
<TestComponent TItem="TestComponent<string>" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11718")]
public void GenericInference_DynamicallyAccessedMembers_01()
{
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Forms
<InputRadioGroup @bind-Value="value1">
<InputRadio Value="@("false")" />
<InputRadio Value="@("true")" />
</InputRadioGroup>
@code {
private string value1 = "true";
}
""");
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
Assert.Contains("DynamicallyAccessedMembers", generated.Code);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11718")]
public void GenericInference_DynamicallyAccessedMembers_02()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
using System;
using System.Diagnostics.CodeAnalysis;
using DAM = System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute;
using DAMT = System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes;
namespace Test;
public class MyComponent<T1,
[Attr, DAM(DAMT.PublicMethods | DAMT.PublicFields)] T2,
[DAM(DAMT.None)] [x: DAM(DAMT.All)] T3>
: ComponentBase
{
[Parameter] public required T1 P1 { get; set; }
[Parameter] public required T2 P2 { get; set; }
[Parameter] public required T3 P3 { get; set; }
}
class Attr : Attribute;
"""));
var expectedDiagnostics = new[]
{
// (9,23): warning CS0658: 'x' is not a recognized attribute location. Valid attribute locations for this declaration are 'typevar'. All attributes in this block will be ignored.
// [DAM(DAMT.None)] [x: DAM(DAMT.All)] T3>
Diagnostic(ErrorCode.WRN_InvalidAttributeLocation, "x").WithArguments("x", "typevar").WithLocation(9, 23)
};
var generated = CompileToCSharp("""
<MyComponent P1="s" P2="2" P3="s" />
@code {
private string s = "x";
}
""", expectedDiagnostics);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, expectedDiagnostics);
Assert.Contains("DynamicallyAccessedMembers", generated.Code);
}
#endregion
#region Key
[IntegrationTestFact]
public void Element_WithKey()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<elem attributebefore=""before"" @key=""someObject"" attributeafter=""after"">Hello</elem>
@code {
private object someObject = new object();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Element_WithKey_AndOtherAttributes()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<input type=""text"" data-slider-min=""@Min"" @key=""@someObject"" />
@code {
private object someObject = new object();
[Parameter] public int Min { get; set; }
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithKey()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Arrange/Act
var generated = CompileToCSharp(@"
<MyComponent ParamBefore=""before"" @key=""someDate.Day"" ParamAfter=""after"" />
@code {
private DateTime someDate = DateTime.Now;
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithKey_WithChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Arrange/Act
var generated = CompileToCSharp(@"
<MyComponent @key=""123 + 456"" SomeProp=""val"">
Some <el>further</el> content
</MyComponent>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Element_WithKey_AttributeNameIsCaseSensitive()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<elem attributebefore=""before"" @KEY=""someObject"" attributeafter=""after"">Hello</elem>
@code {
private object someObject = new object();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region Splat
[IntegrationTestFact]
public void Element_WithSplat()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<elem attributebefore=""before"" @attributes=""someAttributes"" attributeafter=""after"">Hello</elem>
@code {
private Dictionary<string, object> someAttributes = new Dictionary<string, object>();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Element_WithSplat_ImplicitExpression()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<elem attributebefore=""before"" @attributes=""@someAttributes"" attributeafter=""after"">Hello</elem>
@code {
private Dictionary<string, object> someAttributes = new Dictionary<string, object>();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Element_WithSplat_ExplicitExpression()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<elem attributebefore=""before"" @attributes=""@(someAttributes)"" attributeafter=""after"">Hello</elem>
@code {
private Dictionary<string, object> someAttributes = new Dictionary<string, object>();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithSplat()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Arrange/Act
var generated = CompileToCSharp(@"
<MyComponent AttributeBefore=""before"" @attributes=""someAttributes"" AttributeAfter=""after"" />
@code {
private Dictionary<string, object> someAttributes = new Dictionary<string, object>();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithSplat_ImplicitExpression()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Arrange/Act
var generated = CompileToCSharp(@"
<MyComponent AttributeBefore=""before"" @attributes=""@someAttributes"" AttributeAfter=""after"" />
@code {
private Dictionary<string, object> someAttributes = new Dictionary<string, object>();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithSplat_ExplicitExpression()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Arrange/Act
var generated = CompileToCSharp(@"
<MyComponent AttributeBefore=""before"" @attributes=""@(someAttributes)"" AttributeAfter=""after"" />
@code {
private Dictionary<string, object> someAttributes = new Dictionary<string, object>();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithSplat_GenericTypeInference()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent<T> : ComponentBase
{
[Parameter] public T Value { get; set;}
}
}
"));
// Arrange/Act
var generated = CompileToCSharp(@"
<MyComponent Value=""18"" @attributes=""@(someAttributes)"" />
@code {
private Dictionary<string, object> someAttributes = new Dictionary<string, object>();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Element_WithSplat_AttributeNameIsCaseSensitive()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<elem attributebefore=""before"" @ATTributes=""someAttributes"" attributeafter=""after"">Hello</elem>
@code {
private Dictionary<string, object> someAttributes = new Dictionary<string, object>();
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region Ref
[IntegrationTestFact]
public void Element_WithRef()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<elem attributebefore=""before"" @ref=""myElem"" attributeafter=""after"">Hello</elem>
@code {
private Microsoft.AspNetCore.Components.ElementReference myElem;
public void Foo() { System.GC.KeepAlive(myElem); }
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Element_WithRef_AndOtherAttributes()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<input type=""text"" data-slider-min=""@Min"" @ref=""@_element"" />
@code {
private ElementReference _element;
[Parameter] public int Min { get; set; }
public void Foo() { System.GC.KeepAlive(_element); }
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithRef()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Arrange/Act
var generated = CompileToCSharp(@"
<MyComponent ParamBefore=""before"" @ref=""myInstance"" ParamAfter=""after"" />
@code {
private Test.MyComponent myInstance;
public void Foo() { System.GC.KeepAlive(myInstance); }
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/8170")]
public void Component_WithRef_Nullable()
{
// Act
var generated = CompileToCSharp("""
<TestComponent @ref="myComponent" />
@code {
private TestComponent myComponent = null!;
public void Use() { System.GC.KeepAlive(myComponent); }
}
""",
nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/8170")]
public void Component_WithRef_Nullable_Generic()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyComponent<T> : ComponentBase
{
[Parameter] public T MyParameter { get; set; } = default!;
}
"""));
// Act
var generated = CompileToCSharp("""
<MyComponent @ref="myComponent" MyParameter="1" />
@code {
private MyComponent<int> myComponent = null!;
public void Use() { System.GC.KeepAlive(myComponent); }
}
""",
nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_WithRef_WithChildContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
}
}
"));
// Arrange/Act
var generated = CompileToCSharp(@"
<MyComponent @ref=""myInstance"" SomeProp=""val"">
Some <el>further</el> content
</MyComponent>
@code {
private Test.MyComponent myInstance;
public void Foo() { System.GC.KeepAlive(myInstance); }
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Element_WithRef_AttributeNameIsCaseSensitive()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<elem attributebefore=""before"" @rEF=""myElem"" attributeafter=""after"">Hello</elem>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9625")]
public void Component_WithRef_Generic_SystemInNamespace()
{
var generated = CompileToCSharp("""
@namespace X.Y.System.Z
@typeparam T
<TestComponent Param="Param" @ref="comp" />
@code {
private TestComponent<T?>? comp;
[Parameter] public T? Param { get; set; }
}
""", nullableEnable: true);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/12043")]
public void Component_WithRef_NamespaceConflict()
{
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Y
{
public class MyComponent : ComponentBase;
}
namespace X.Y
{
public static class E
{
public static int M() => 123;
}
}
"""));
var generated = CompileToCSharp("""
@using global::Y
@using global::X.Y
@namespace X
@E.M()
<MyComponent @ref="comp" />
@code {
private MyComponent comp;
}
""");
var expectedDiagnostics = DesignTime
? new[]
{
// x:\dir\subdir\Test\TestComponent.cshtml(9,25): warning CS0414: The field 'TestComponent.comp' is assigned but its value is never used
// private MyComponent comp;
Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "comp").WithArguments("X.TestComponent.comp"),
}
: [];
CompileToAssembly(generated, expectedDiagnostics);
}
#endregion
#region Templates
[IntegrationTestFact]
public void RazorTemplate_InCodeBlock()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@{
RenderFragment<Person> p = (person) => @<div>@person.Name</div>;
}
@code {
class Person
{
public string Name { get; set; }
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RazorTemplate_InExplicitExpression()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@(RenderPerson((person) => @<div>@person.Name</div>))
@code {
class Person
{
public string Name { get; set; }
}
object RenderPerson(RenderFragment<Person> p) => null;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RazorTemplate_NonGeneric_InImplicitExpression()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@RenderPerson(@<div>HI</div>)
@code {
object RenderPerson(RenderFragment p) => null;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RazorTemplate_Generic_InImplicitExpression()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@RenderPerson((person) => @<div>@person.Name</div>)
@code {
class Person
{
public string Name { get; set; }
}
object RenderPerson(RenderFragment<Person> p) => null;
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RazorTemplate_ContainsComponent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public string Name { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@{
RenderFragment<Person> p = (person) => @<div><MyComponent Name=""@person.Name""/></div>;
}
@code {
class Person
{
public string Name { get; set; }
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
// Targeted at the logic that assigns 'builder' names
[IntegrationTestFact]
public void RazorTemplate_FollowedByComponent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public string Name { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@{
RenderFragment<Person> p = (person) => @<div><MyComponent Name=""@person.Name""/></div>;
}
<MyComponent>
@(""hello, world!"")
</MyComponent>
@code {
class Person
{
public string Name { get; set; }
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RazorTemplate_NonGeneric_AsComponentParameter()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public RenderFragment Template { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@{ RenderFragment template = @<div>Joey</div>; }
<MyComponent Person=""@template""/>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RazorTemplate_Generic_AsComponentParameter()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public RenderFragment<Person> PersonTemplate { get; set; }
}
public class Person
{
public string Name { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@{ RenderFragment<Person> template = (person) => @<div>@person.Name</div>; }
<MyComponent PersonTemplate=""@template""/>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RazorTemplate_AsComponentParameter_MixedContent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public RenderFragment<Context> Template { get; set; }
}
public class Context
{
public int Index { get; set; }
public string Item { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@{ RenderFragment<Test.Context> template = (context) => @<li>#@context.Index - @context.Item.ToLower()</li>; }
<MyComponent Template=""@template""/>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region Whitespace
[IntegrationTestFact]
public void LeadingWhiteSpace_WithDirective()
{
// Arrange/Act
var generated = CompileToCSharp(@"
@using System
<h1>Hello</h1>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void LeadingWhiteSpace_WithCSharpExpression()
{
// Arrange/Act
var generated = CompileToCSharp(@"
@(""My value"")
<h1>Hello</h1>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void LeadingWhiteSpace_WithComponent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class SomeOtherComponent : ComponentBase
{
[Parameter] public RenderFragment ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<SomeOtherComponent>
<h1>Child content at @DateTime.Now</h1>
<p>Very @(""good"")</p>
</SomeOtherComponent>
<h1>Hello</h1>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void TrailingWhiteSpace_WithDirective()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<h1>Hello</h1>
@page ""/my/url""
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void TrailingWhiteSpace_WithCSharpExpression()
{
// Arrange/Act
var generated = CompileToCSharp(@"
<h1>Hello</h1>
@(""My value"")
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void TrailingWhiteSpace_WithComponent()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class SomeOtherComponent : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<h1>Hello</h1>
<SomeOtherComponent />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Whitespace_BetweenElementAndFunctions()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<elem attr=@Foo />
@code {
int Foo = 18;
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void WhiteSpace_InsideAttribute_InMarkupBlock()
{
// Arrange
// Act
var generated = CompileToCSharp(@"<div class=""first second"">Hello</div>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void WhiteSpace_InMarkupInFunctionsBlock()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Rendering
@code {
void MyMethod(RenderTreeBuilder __builder)
{
<ul>
@for (var i = 0; i < 100; i++)
{
<li>
@i
</li>
}
</ul>
}
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void WhiteSpace_WithPreserveWhitespace()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@preservewhitespace true
<elem attr=@Foo>
<child />
</elem>
@code {
int Foo = 18;
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region Legacy 3.1 Whitespace
[IntegrationTestFact]
public void Legacy_3_1_LeadingWhiteSpace_WithDirective()
{
// Arrange/Act
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_3_0 };
var generated = CompileToCSharp(@"
@using System
<h1>Hello</h1>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Legacy_3_1_LeadingWhiteSpace_WithCSharpExpression()
{
// Arrange/Act
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_3_0 };
var generated = CompileToCSharp(@"
@(""My value"")
<h1>Hello</h1>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Legacy_3_1_LeadingWhiteSpace_WithComponent()
{
// Arrange
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_3_0 };
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class SomeOtherComponent : ComponentBase
{
[Parameter] public RenderFragment ChildContent { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<SomeOtherComponent>
<h1>Child content at @DateTime.Now</h1>
<p>Very @(""good"")</p>
</SomeOtherComponent>
<h1>Hello</h1>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Legacy_3_1_TrailingWhiteSpace_WithDirective()
{
// Arrange/Act
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_3_0 };
var generated = CompileToCSharp(@"
<h1>Hello</h1>
@page ""/my/url""
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Legacy_3_1_TrailingWhiteSpace_WithCSharpExpression()
{
// Arrange/Act
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_3_0 };
var generated = CompileToCSharp(@"
<h1>Hello</h1>
@(""My value"")
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Legacy_3_1_TrailingWhiteSpace_WithComponent()
{
// Arrange
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_3_0 };
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class SomeOtherComponent : ComponentBase
{
}
}
"));
// Act
var generated = CompileToCSharp(@"
<h1>Hello</h1>
<SomeOtherComponent />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Legacy_3_1_Whitespace_BetweenElementAndFunctions()
{
// Arrange
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_3_0 };
// Act
var generated = CompileToCSharp(@"
<elem attr=@Foo />
@code {
int Foo = 18;
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Legacy_3_1_WhiteSpace_InsideAttribute_InMarkupBlock()
{
// Arrange
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_3_0 };
// Act
var generated = CompileToCSharp(@"<div class=""first second"">Hello</div>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Legacy_3_1_WhiteSpace_InMarkupInFunctionsBlock()
{
// Arrange
_configuration = base.Configuration with { LanguageVersion = RazorLanguageVersion.Version_3_0 };
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Rendering
@code {
void MyMethod(RenderTreeBuilder __builder)
{
<ul>
@for (var i = 0; i < 100; i++)
{
<li>
@i
</li>
}
</ul>
}
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region Imports
[IntegrationTestFact]
public void Component_WithImportsFile()
{
// Arrange
var importContent = @"
@using System.Text
@using System.Reflection
@attribute [Serializable]
";
var importItem = CreateProjectItem("_Imports.razor", importContent, RazorFileKind.ComponentImport);
ImportItems.Add(importItem);
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class Counter : ComponentBase
{
public int Count { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Counter />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ComponentImports()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
namespace Test
{
public class MainLayout : ComponentBase, ILayoutComponent
{
public RenderFragment Body { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp("_Imports.razor", @"
@using System.Text
@using System.Reflection
@layout MainLayout
@Foo
<div>Hello</div>
", fileKind: RazorFileKind.ComponentImport, expectedCSharpDiagnostics: [
// (4,31): error CS0246: The type or namespace name 'ComponentBase' could not be found (are you missing a using directive or an assembly reference?)
// public class MainLayout : ComponentBase, ILayoutComponent
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "ComponentBase").WithArguments("ComponentBase").WithLocation(4, 31),
// (4,46): error CS0246: The type or namespace name 'ILayoutComponent' could not be found (are you missing a using directive or an assembly reference?)
// public class MainLayout : ComponentBase, ILayoutComponent
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "ILayoutComponent").WithArguments("ILayoutComponent").WithLocation(4, 46),
// (6,16): error CS0246: The type or namespace name 'RenderFragment' could not be found (are you missing a using directive or an assembly reference?)
// public RenderFragment Body { get; set; }
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "RenderFragment").WithArguments("RenderFragment").WithLocation(6, 16)]);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, DesignTime
? [// (4,31): error CS0246: The type or namespace name 'ComponentBase' could not be found (are you missing a using directive or an assembly reference?)
// public class MainLayout : ComponentBase, ILayoutComponent
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "ComponentBase").WithArguments("ComponentBase").WithLocation(4, 31),
// (4,46): error CS0246: The type or namespace name 'ILayoutComponent' could not be found (are you missing a using directive or an assembly reference?)
// public class MainLayout : ComponentBase, ILayoutComponent
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "ILayoutComponent").WithArguments("ILayoutComponent").WithLocation(4, 46),
// (6,16): error CS0246: The type or namespace name 'RenderFragment' could not be found (are you missing a using directive or an assembly reference?)
// public RenderFragment Body { get; set; }
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "RenderFragment").WithArguments("RenderFragment").WithLocation(6, 16),
// x:\dir\subdir\Test\_Imports.razor(5,2): error CS0103: The name 'Foo' does not exist in the current context
// Foo
Diagnostic(ErrorCode.ERR_NameNotInContext, "Foo").WithArguments("Foo").WithLocation(5, 7)]
: [// (4,31): error CS0246: The type or namespace name 'ComponentBase' could not be found (are you missing a using directive or an assembly reference?)
// public class MainLayout : ComponentBase, ILayoutComponent
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "ComponentBase").WithArguments("ComponentBase").WithLocation(4, 31),
// (4,46): error CS0246: The type or namespace name 'ILayoutComponent' could not be found (are you missing a using directive or an assembly reference?)
// public class MainLayout : ComponentBase, ILayoutComponent
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "ILayoutComponent").WithArguments("ILayoutComponent").WithLocation(4, 46),
// (6,16): error CS0246: The type or namespace name 'RenderFragment' could not be found (are you missing a using directive or an assembly reference?)
// public RenderFragment Body { get; set; }
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "RenderFragment").WithArguments("RenderFragment").WithLocation(6, 16),
// x:\dir\subdir\Test\_Imports.razor(5,2): error CS0103: The name 'Foo' does not exist in the current context
// Foo
Diagnostic(ErrorCode.ERR_NameNotInContext, "Foo").WithArguments("Foo").WithLocation(5, 2),
// x:\dir\subdir\Test\_Imports.razor(5,2): error CS0103: The name '__builder' does not exist in the current context
// __builder.AddContent(0, Foo
Diagnostic(ErrorCode.ERR_NameNotInContext, "__builder").WithArguments("__builder").WithLocation(5, 2)]);
}
[IntegrationTestFact]
public void ComponentImports_GlobalPrefix()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace MyComponents
{
public class Counter : ComponentBase
{
}
}
"""));
// Act
var generated = CompileToCSharp("Index.razor", cshtmlContent: """
@using global::MyComponents
<Counter />
""");
// Assert
CompileToAssembly(generated);
Assert.DoesNotContain("<Counter", generated.Code);
Assert.Contains("global::MyComponents.Counter", generated.Code);
}
[IntegrationTestFact]
public void Component_NamespaceDirective_InImports()
{
// Arrange
var importContent = @"
@using System.Text
@using System.Reflection
@namespace New.Test
";
var importItem = CreateProjectItem("_Imports.razor", importContent, RazorFileKind.ComponentImport);
ImportItems.Add(importItem);
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace New.Test
{
public class Counter : ComponentBase
{
public int Count { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Counter />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_NamespaceDirective_OverrideImports()
{
// Arrange
var importContent = @"
@using System.Text
@using System.Reflection
@namespace Import.Test
";
var importItem = CreateProjectItem("_Imports.razor", importContent, RazorFileKind.ComponentImport);
ImportItems.Add(importItem);
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace New.Test
{
public class Counter2 : ComponentBase
{
public int Count { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp("Pages/Counter.razor", cshtmlContent: @"
@namespace New.Test
<Counter2 />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/7091")]
public void Component_NamespaceDirective_ContainsSystem()
{
// Act
var generated = CompileToCSharp("""
@namespace X.System.Y
""");
// Assert
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_PreserveWhitespaceDirective_InImports()
{
// Arrange
var importContent = @"
@preservewhitespace true
";
var importItem = CreateProjectItem("_Imports.razor", importContent, RazorFileKind.ComponentImport);
ImportItems.Add(importItem);
// Act
var generated = CompileToCSharp(@"
<parent>
<child> @DateTime.Now </child>
</parent>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_PreserveWhitespaceDirective_OverrideImports()
{
// Arrange
var importContent = @"
@preservewhitespace true
";
var importItem = CreateProjectItem("_Imports.razor", importContent, RazorFileKind.ComponentImport);
ImportItems.Add(importItem);
// Act
var generated = CompileToCSharp(@"
@preservewhitespace false
<parent>
<child> @DateTime.Now </child>
</parent>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region Namespace
[IntegrationTestFact]
public void EmptyRootNamespace()
{
DefaultRootNamespace = string.Empty;
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
public class Component1 : ComponentBase { }
namespace Shared
{
public class Component2 : ComponentBase { }
}
class C
{
void M1(TestComponent t) { }
void M2(global::TestComponent t) { }
}
"""));
var generated = CompileToCSharp("""
<h1>Generated</h1>
<Component1 />
<Shared.Component2 />
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void NamespaceWithSurrogatePair()
{
DefaultRootNamespace = "test𝔸namespace";
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace test_namespace
{
public class Component1 : ComponentBase { }
namespace Shared
{
public class Component2 : ComponentBase { }
}
}
"""));
var generated = CompileToCSharp("""
<h1>Generated</h1>
<Component1 />
<Shared.Component2 />
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region "CSS scoping"
[IntegrationTestFact]
public void Component_WithCssScope()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class TemplatedComponent : ComponentBase
{
[Parameter]
public RenderFragment ChildContent { get; set; }
}
}
"));
// Act
// This test case attempts to use all syntaxes that might interact with auto-generated attributes
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Rendering
<h1>Element with no attributes</h1>
<parent with-attributes=""yes"" with-csharp-attribute-value=""@(123)"">
<child />
<child has multiple attributes=""some with values"">With text</child>
<TemplatedComponent @ref=""myComponentReference"">
<span id=""hello"">This is in child content</span>
</TemplatedComponent>
</parent>
@if (DateTime.Now.Year > 1950)
{
<with-ref-capture some-attr @ref=""myElementReference"">Content</with-ref-capture>
<input id=""myElem"" @bind=""myVariable"" another-attr=""Another attr value"" />
}
@code {
ElementReference myElementReference;
TemplatedComponent myComponentReference;
string myVariable;
void MethodRenderingMarkup(RenderTreeBuilder __builder)
{
for (var i = 0; i < 10; i++)
{
<li data-index=@i>Something @i</li>
}
System.GC.KeepAlive(myElementReference);
System.GC.KeepAlive(myComponentReference);
System.GC.KeepAlive(myVariable);
}
}
", cssScope: "TestCssScope");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region Misc
[IntegrationTestFact] // We don't process <!DOCTYPE ...> - we just skip them
public void Component_WithDocType()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<!DOCTYPE html>
<div>
</div>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void DuplicateMarkupAttributes_IsAnError()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<div>
<a href=""/cool-url"" style="""" disabled href=""/even-cooler-url"">Learn the ten cool tricks your compiler author will hate!</a>
</div>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.DuplicateMarkupAttribute.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void DuplicateMarkupAttributes_IsAnError_EventHandler()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<div>
<a onclick=""test()"" @onclick=""() => {}"">Learn the ten cool tricks your compiler author will hate!</a>
</div>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.DuplicateMarkupAttributeDirective.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void DuplicateMarkupAttributes_Multiple_IsAnError()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
<div>
<a href=""/cool-url"" style="""" disabled href=""/even-cooler-url"" href>Learn the ten cool tricks your compiler author will hate!</a>
</div>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
Assert.All(generated.RazorDiagnostics, d =>
{
Assert.Same(ComponentDiagnosticFactory.DuplicateMarkupAttribute.Id, d.Id);
});
}
[IntegrationTestFact]
public void DuplicateMarkupAttributes_IsAnError_BindValue()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<div>
<input type=""text"" value=""17"" @bind=""@text""></input>
</div>
@functions {
private string text = ""hi"";
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.DuplicateMarkupAttributeDirective.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void DuplicateMarkupAttributes_DifferentCasing_IsAnError_BindValue()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<div>
<input type=""text"" Value=""17"" @bind=""@text""></input>
</div>
@functions {
private string text = ""hi"";
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.DuplicateMarkupAttributeDirective.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void DuplicateMarkupAttributes_IsAnError_BindOnInput()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<div>
<input type=""text"" @bind-value=""@text"" @bind-value:event=""oninput"" @oninput=""() => {}""></input>
</div>
@functions {
private string text = ""hi"";
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.DuplicateMarkupAttributeDirective.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void DuplicateComponentParameters_IsAnError()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public string Message { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Message=""test"" mESSAGE=""test"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.DuplicateComponentParameter.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void DuplicateComponentParameters_IsAnError_Multiple()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public string Message { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Message=""test"" mESSAGE=""test"" Message=""anotherone"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
Assert.All(generated.RazorDiagnostics, d =>
{
Assert.Same(ComponentDiagnosticFactory.DuplicateComponentParameter.Id, d.Id);
});
}
[IntegrationTestFact]
public void DuplicateComponentParameters_IsAnError_WeaklyTyped()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public string Message { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Foo=""test"" foo=""test"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.DuplicateComponentParameter.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void DuplicateComponentParameters_IsAnError_BindMessage()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public string Message { get; set; }
[Parameter] public EventCallback<string> MessageChanged { get; set; }
[Parameter] public Expression<Action<string>> MessageExpression { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent Message=""@message"" @bind-Message=""@message"" />
@functions {
string message = ""hi"";
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.DuplicateComponentParameterDirective.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void DuplicateComponentParameters_IsAnError_BindMessageChanged()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public string Message { get; set; }
[Parameter] public EventCallback<string> MessageChanged { get; set; }
[Parameter] public Expression<Action<string>> MessageExpression { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent MessageChanged=""@((s) => {})"" @bind-Message=""@message"" />
@functions {
string message = ""hi"";
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.DuplicateComponentParameterDirective.Id, diagnostic.Id);
}
[IntegrationTestFact]
public void DuplicateComponentParameters_IsAnError_BindMessageExpression()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public string Message { get; set; }
[Parameter] public EventCallback<string> MessageChanged { get; set; }
[Parameter] public Expression<Action<string>> MessageExpression { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent @bind-Message=""@message"" MessageExpression=""@((s) => {})"" />
@functions {
string message = ""hi"";
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
var diagnostic = Assert.Single(generated.RazorDiagnostics);
Assert.Same(ComponentDiagnosticFactory.DuplicateComponentParameterDirective.Id, diagnostic.Id);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/blazor/issues/597")]
public void Regression_597()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class Counter : ComponentBase
{
public int Count { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Counter @bind-v=""y"" />
@code {
string y = null;
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Regression_609()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using System;
using Microsoft.AspNetCore.Components;
namespace Test
{
public class User : ComponentBase
{
public string Name { get; set; }
public Action<string> NameChanged { get; set; }
public bool IsActive { get; set; }
public Action<bool> IsActiveChanged { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<User @bind-Name=""@UserName"" @bind-IsActive=""@UserIsActive"" />
@code {
public string UserName { get; set; }
public bool UserIsActive { get; set; }
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/blazor/issues/772")]
public void Regression_772()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class SurveyPrompt : ComponentBase
{
[Parameter] public string Title { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@page ""/""
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title=""
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
// This has some errors
Assert.Collection(
generated.RazorDiagnostics.OrderBy(d => d.Id),
d => Assert.Equal("RZ1034", d.Id),
d => Assert.Equal("RZ1035", d.Id));
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/blazor/issues/773")]
public void Regression_773()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class SurveyPrompt : ComponentBase
{
[Parameter] public string Title { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
@page ""/""
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title=""<div>Test!</div>"" />
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Regression_784()
{
// Arrange
// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components.Web
<p @onmouseover=""OnComponentHover"" style=""background: @ParentBgColor;"" />
@code {
public string ParentBgColor { get; set; } = ""#FFFFFF"";
public void OnComponentHover(MouseEventArgs e)
{
}
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void EventHandlerTagHelper_EscapeQuotes()
{
// Act
var generated = CompileToCSharp(@"
<input onfocus='alert(""Test"");' />
<input onfocus=""alert(""Test"");"" />
<input onfocus=""alert('Test');"" />
<p data-options='{direction: ""fromtop"", animation_duration: 25, direction: ""reverse""}'></p>
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_ComplexContentInAttribute()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public string StringProperty { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp("""
<MyComponent StringProperty="@MyEnum." />
@code {
public enum MyEnum
{
One,
Two
}
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(1,31): error CS0119: 'TestComponent.MyEnum' is a type, which is not valid in the given context
// MyEnum
Diagnostic(ErrorCode.ERR_BadSKunknown, "MyEnum").WithArguments("Test.TestComponent.MyEnum", "type").WithLocation(1, 31));
Assert.NotEmpty(generated.RazorDiagnostics);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9346")]
public void Component_ComplexContentInAttribute_02()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public string StringProperty { get; set; }
}
}
"""));
// Act
var generated = CompileToCSharp("""
<MyComponent StringProperty="@MyEnum+" />
@code {
public enum MyEnum
{
One,
Two
}
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(1,31): error CS0119: 'TestComponent.MyEnum' is a type, which is not valid in the given context
// MyEnum
Diagnostic(ErrorCode.ERR_BadSKunknown, "MyEnum").WithArguments("Test.TestComponent.MyEnum", "type").WithLocation(1, 31));
Assert.NotEmpty(generated.RazorDiagnostics);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9346")]
public void Component_ComplexContentInAttribute_03()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public string StringProperty { get; set; }
}
}
"""));
// Act
var generated = CompileToCSharp("""
<MyComponent StringProperty="@x html @("string")" />
@code {
int x = 1;
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, DesignTime
? [// x:\dir\subdir\Test\TestComponent.cshtml(1,32): error CS1003: Syntax error, ',' expected
// x
Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(1, 32),
// (27,91): error CS1501: No overload for method 'TypeCheck' takes 2 arguments
// __o = global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<global::System.String>(
Diagnostic(ErrorCode.ERR_BadArgCount, "TypeCheck<global::System.String>").WithArguments("TypeCheck", "2").WithLocation(27, 91)]
: [// x:\dir\subdir\Test\TestComponent.cshtml(1,32): error CS1003: Syntax error, ',' expected
// x
Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(1, 32),
// (29,88): error CS1501: No overload for method 'TypeCheck' takes 2 arguments
// __o = global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<global::System.String>(
Diagnostic(ErrorCode.ERR_BadArgCount, "TypeCheck<global::System.String>").WithArguments("TypeCheck", "2").WithLocation(29, 88)]
);
Assert.NotEmpty(generated.RazorDiagnostics);
}
[IntegrationTestFact]
public void Component_TextTagsAreNotRendered()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class Counter : ComponentBase
{
public int Count { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<Counter />
@if (true)
{
<text>This text is rendered</text>
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_MatchingIsCaseSensitive()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public int IntProperty { get; set; }
[Parameter] public bool BoolProperty { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent />
<mycomponent />
<MyComponent intproperty='1' BoolProperty='true' />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Component_MultipleComponentsDifferByCase()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter] public int IntProperty { get; set; }
}
public class Mycomponent : ComponentBase
{
[Parameter] public int IntProperty { get; set; }
}
}
"));
// Act
var generated = CompileToCSharp(@"
<MyComponent IntProperty='1' />
<Mycomponent IntProperty='2' />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ElementWithUppercaseTagName_CanHideWarningWithBang()
{
// Arrange & Act
var generated = CompileToCSharp(@"
<!NotAComponent />
<!DefinitelyNotAComponent></!DefinitelyNotAComponent>");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestTheory, CombinatorialData, WorkItem("https://github.com/dotnet/razor/issues/9584")]
public void ScriptTag_Razor8([CombinatorialValues("8.0", "latest")] string langVersion)
{
var generated = CompileToCSharp("""
<script>alert("Hello");</script>
""",
configuration: Configuration with { LanguageVersion = RazorLanguageVersion.Parse(langVersion) });
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9584")]
public void ScriptTag_Razor7()
{
var generated = CompileToCSharp("""
<script>alert("Hello");</script>
""",
configuration: Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 });
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void AtTransitions()
{
var generated = CompileToCSharp("""
@{
var x = "hello";
@x x = "world"; @x
}
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/sdk/issues/42730")]
public void AtAtHandled()
{
var generated = CompileToCSharp("""
@{ var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div"); }
""");
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(1,28): error CS0103: The name 'Html' does not exist in the current context
// var validationMessage = @Html.ValidationMessage("test", "", new { @@class = "invalid-feedback" }, "div");
Diagnostic(ErrorCode.ERR_NameNotInContext, "@Html").WithArguments("Html").WithLocation(1, 28));
}
#endregion
#region LinePragmas
[IntegrationTestFact]
public void ProducesEnhancedLinePragmaWhenNecessary()
{
var generated = CompileToCSharp(@"
<h1>Single line statement</h1>
Time: @DateTime.Now
<h1>Multiline block statement</h1>
@JsonToHtml(@""{
'key1': 'value1'
'key2': 'value2'
}"")
@code {
public string JsonToHtml(string foo)
{
return foo;
}
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void ProducesStandardLinePragmaForCSharpCode()
{
var generated = CompileToCSharp(@"
<h1>Conditional statement</h1>
@for (var i = 0; i < 10; i++)
{
<p>@i</p>
}
<h1>Statements inside code block</h1>
@{System.Console.WriteLine(1);System.Console.WriteLine(2);}
<h1>Full-on code block</h1>
@code {
[Parameter]
public int IncrementAmount { get; set; }
}
");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void CanProduceLinePragmasForComponentWithRenderFragment_01()
{
var code = @"
<div class=""row"">
<a href=""#"" @onclick=Toggle class=""col-12"">@ActionText</a>
@if (!Collapsed)
{
<div class=""col-12 card card-body"">
@ChildContent
</div>
}
</div>
@code
{
[Parameter]
public RenderFragment ChildContent { get; set; } = (context) => <p>@context</p>
[Parameter]
public bool Collapsed { get; set; }
string ActionText { get => Collapsed ? ""Expand"" : ""Collapse""; }
void Toggle()
{
Collapsed = !Collapsed;
}
}";
DiagnosticDescription[] expectedDiagnostics = [
// x:\dir\subdir\Test\TestComponent.cshtml(13,67): error CS1525: Invalid expression term '<'
// public RenderFragment ChildContent { get; set; } = (context) => <p>@context</p>
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "<").WithArguments("<").WithLocation(13, 67),
// x:\dir\subdir\Test\TestComponent.cshtml(13,67): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
// public RenderFragment ChildContent { get; set; } = (context) => <p>@context</p>
Diagnostic(ErrorCode.ERR_IllegalStatement, """
<p>@context</p>
[Parameter]
""".NormalizeLineEndings()).WithLocation(13, 67),
// x:\dir\subdir\Test\TestComponent.cshtml(13,68): error CS0103: The name 'p' does not exist in the current context
// public RenderFragment ChildContent { get; set; } = (context) => <p>@context</p>
Diagnostic(ErrorCode.ERR_NameNotInContext, "p").WithArguments("p").WithLocation(13, 68),
// x:\dir\subdir\Test\TestComponent.cshtml(13,79): error CS1525: Invalid expression term '/'
// public RenderFragment ChildContent { get; set; } = (context) => <p>@context</p>
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "/").WithArguments("/").WithLocation(13, 79),
// x:\dir\subdir\Test\TestComponent.cshtml(13,80): error CS0103: The name 'p' does not exist in the current context
// public RenderFragment ChildContent { get; set; } = (context) => <p>@context</p>
Diagnostic(ErrorCode.ERR_NameNotInContext, "p").WithArguments("p").WithLocation(13, 80),
// x:\dir\subdir\Test\TestComponent.cshtml(14,4): error CS0103: The name 'Parameter' does not exist in the current context
// [Parameter]
Diagnostic(ErrorCode.ERR_NameNotInContext, "Parameter").WithArguments("Parameter").WithLocation(14, 4),
// x:\dir\subdir\Test\TestComponent.cshtml(14,14): error CS1002: ; expected
// [Parameter]
Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(14, 14)]
;
var generated = CompileToCSharp(code, expectedDiagnostics);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, expectedDiagnostics);
}
[IntegrationTestFact]
public void CanProduceLinePragmasForComponentWithRenderFragment_02()
{
var generated = CompileToCSharp(@"
<div class=""row"">
<a href=""#"" @onclick=Toggle class=""col-12"">@ActionText</a>
@if (!Collapsed)
{
<div class=""col-12 card card-body"">
@ChildContent
</div>
}
</div>
@code
{
[Parameter]
public RenderFragment<string> ChildContent { get; set; } = (context) => @<p>@context</p>;
[Parameter]
public bool Collapsed { get; set; }
string ActionText { get => Collapsed ? ""Expand"" : ""Collapse""; }
void Toggle()
{
Collapsed = !Collapsed;
}
}");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9359")]
public void LinePragma_Multiline()
{
// Act
var generated = CompileToCSharp("""
@("text"
)
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
#region RenderMode
[IntegrationTestFact]
public void RenderMode_Directive_WithTypeParam()
{
var generated = CompileToCSharp("""
@typeparam T
@rendermode Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Directive_WithTypeParam_Razor9()
{
var generated = CompileToCSharp("""
@typeparam T
@rendermode Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer
""",
configuration: Configuration with { LanguageVersion = RazorLanguageVersion.Version_9_0 },
expectedCSharpDiagnostics:
// (17,19): error CS0305: Using the generic type 'TestComponent<T>' requires 1 type arguments
// [global::Test.TestComponent.__PrivateComponentRenderModeAttribute]
Diagnostic(ErrorCode.ERR_BadArity, "TestComponent").WithArguments("Test.TestComponent<T>", "type", "1").WithLocation(17, 19));
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
// (13,19): error CS0305: Using the generic type 'TestComponent<T>' requires 1 type arguments
// [global::Test.TestComponent.__PrivateComponentRenderModeAttribute]
Diagnostic(ErrorCode.ERR_BadArity, "TestComponent").WithArguments("Test.TestComponent<T>", "type", "1").WithLocation(13, 19));
}
[IntegrationTestFact]
public void RenderMode_Directive_WithTypeParam_First()
{
var generated = CompileToCSharp("""
@rendermode Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer
@typeparam T
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Directive_FullyQualified()
{
var generated = CompileToCSharp("""
@rendermode Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Directive_SimpleExpression()
{
var generated = CompileToCSharp("""
@rendermode @(Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer)
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Directive_SimpleExpression_With_Code()
{
var generated = CompileToCSharp("""
@rendermode @(Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer)
@code
{
[Parameter]
public int Count { get; set; }
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Directive_SimpleExpression_NotFirst()
{
var generated = CompileToCSharp("""
@code
{
[Parameter]
public int Count { get; set; }
}
@rendermode @(Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer)
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Directive_NewExpression()
{
var generated = CompileToCSharp("""
@rendermode @(new TestComponent.MyRenderMode("This is some text"))
@code
{
#pragma warning disable CS9113
public class MyRenderMode(string Text) : Microsoft.AspNetCore.Components.IComponentRenderMode { }
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Directive_WithNamespaces()
{
var generated = CompileToCSharp("""
@namespace Custom.Namespace
@rendermode Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Attribute_With_SimpleIdentifier()
{
var generated = CompileToCSharp($"""
<{ComponentName} @rendermode="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Attribute_With_Expression()
{
var generated = CompileToCSharp($$"""
<{{ComponentName}} @rendermode="@(new MyRenderMode() { Extra = "Hello" })" />
@code
{
class MyRenderMode : Microsoft.AspNetCore.Components.IComponentRenderMode
{
public string Extra {get;set;}
}
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Attribute_With_Existing_Attributes()
{
var generated = CompileToCSharp($$"""
<{{ComponentName}} P2="abc" @rendermode="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer" P1="def" />
@code
{
[Parameter]public string P1 {get; set;}
[Parameter]public string P2 {get; set;}
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void Duplicate_RenderMode()
{
var generated = CompileToCSharp($$"""
<{{ComponentName}} @rendermode="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer"
@rendermode="Value2" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Multiple_Components()
{
var generated = CompileToCSharp($$"""
<{{ComponentName}} @rendermode="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer" />
<{{ComponentName}} @rendermode="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_Child_Components()
{
var generated = CompileToCSharp($$"""
<{{ComponentName}} @rendermode="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer">
<{{ComponentName}} @rendermode="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer">
<{{ComponentName}} @rendermode="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer" />
</{{ComponentName}}>
<{{ComponentName}} @rendermode="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer">
<{{ComponentName}} @rendermode="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer" />
<{{ComponentName}} @rendermode="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer" />
</{{ComponentName}}>
</{{ComponentName}}>
@code
{
[Parameter]
public RenderFragment ChildContent { get; set; }
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_With_TypeInference()
{
var generated = CompileToCSharp($$"""
@typeparam TRenderMode where TRenderMode : Microsoft.AspNetCore.Components.IComponentRenderMode
<{{ComponentName}} @rendermode="RenderModeParam" RenderModeParam="Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer" />
@code
{
[Parameter] public TRenderMode RenderModeParam { get; set;}
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void RenderMode_With_Ternary()
{
var generated = CompileToCSharp($$"""
<{{ComponentName}} @rendermode="@(true ? Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer : null)" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9343")]
public void RenderMode_With_Null_Nullable_Disabled()
{
var generated = CompileToCSharp($$"""
<{{ComponentName}} @rendermode="null" />
""", nullableEnable: false);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9343")]
public void RenderMode_With_Null_Nullable_Enabled()
{
var generated = CompileToCSharp($$"""
<{{ComponentName}} @rendermode="null" />
""", nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9343")]
public void RenderMode_With_Nullable_Receiver()
{
var generated = CompileToCSharp($$"""
@code
{
public class RenderModeContainer
{
public Microsoft.AspNetCore.Components.IComponentRenderMode RenderMode => Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer;
}
RenderModeContainer? Container => null;
}
<{{ComponentName}} @rendermode="@(Container.RenderMode)" />
""", nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
DesignTime
// x:\dir\subdir\Test\TestComponent.cshtml(10,29): warning CS8602: Dereference of a possibly null reference.
// Container.RenderMode
? Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Container").WithLocation(10, 29)
// x:\dir\subdir\Test\TestComponent.cshtml(10,31): warning CS8602: Dereference of a possibly null reference.
// Container.RenderMode
: Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Container").WithLocation(10, 31)
);
}
#endregion
#region FormName
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_HtmlValue()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="named-form-handler"></form>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_CSharpValue()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="@("named-form-handler")"></form>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_CSharpValue_Integer()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="@x"></form>
@code {
int x = 1;
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(2,55): error CS1503: Argument 1: cannot convert from 'int' to 'string'
// x
Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "string").WithLocation(2, 55));
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_MixedValue()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="start @("literal") @x end"></form>
@code {
int x = 1;
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, DesignTime
? [
// x:\dir\subdir\Test\TestComponent.cshtml(2,74): error CS1503: Argument 1: cannot convert from 'int' to 'string'
// x
Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "string").WithLocation(2, 74)
]
: []);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_Nullability()
{
// This could report a nullability warning, but that's not currently supported in other places, either.
// Tracked by https://github.com/dotnet/razor/issues/7398.
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="@null"></form>
""",
nullableEnable: true);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_CSharpError()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="@x"></form>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
// x:\dir\subdir\Test\TestComponent.cshtml(2,55): error CS0103: The name 'x' does not exist in the current context
// x
Diagnostic(ErrorCode.ERR_NameNotInContext, "x").WithArguments("x").WithLocation(2, 55));
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_RazorError()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="@{ }"></form>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated, DesignTime
? [// (41,85): error CS7036: There is no argument given that corresponds to the required parameter 'value' of 'RuntimeHelpers.TypeCheck<T>(T)'
// global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<string>();
Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "TypeCheck<string>").WithArguments("value", "Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<T>(T)").WithLocation(41, 85)]
: [// (41,85): error CS7036: There is no argument given that corresponds to the required parameter 'value' of 'RuntimeHelpers.TypeCheck<T>(T)'
// global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<string>();
Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "TypeCheck<string>").WithArguments("value", "Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<T>(T)").WithLocation(37, 105)]
);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_NotAForm()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<div method="post" @onsubmit="() => { }" @formname="named-form-handler"></div>
<div method="post" @onsubmit="() => { }" @formname="@("named-form-handler")"></div>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_MissingUsing()
{
// Act
var generated = CompileToCSharp("""
<form method="post" @onsubmit="() => { }" @formname="named-form-handler"></form>
<form method="post" @onsubmit="() => { }" @formname="@("named-form-handler")"></form>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_NotAForm_RazorLangVersion7()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<div method="post" @onsubmit="() => { }" @formname="named-form-handler"></div>
<div method="post" @onsubmit="() => { }" @formname="@("named-form-handler")"></div>
""",
configuration: Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 });
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_MissingSubmit()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @formname="named-form-handler"></form>
<form method="post" @formname="@("named-form-handler")"></form>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_FakeSubmit()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" onsubmit="" @formname="named-form-handler"></form>
<form method="post" onsubmit="" @formname="@("named-form-handler")"></form>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_Component()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<TestComponent method="post" @onsubmit="() => { }" @formname="named-form-handler" />
<TestComponent method="post" @onsubmit="() => { }" @formname="@("named-form-handler")" />
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_Component_RazorLangVersion7()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<TestComponent method="post" @onsubmit="() => { }" @formname="named-form-handler" />
<TestComponent method="post" @onsubmit="() => { }" @formname="@("named-form-handler")" />
""",
configuration: Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 });
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_Component_Generic()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
@typeparam T
<TestComponent method="post" @onsubmit="() => { }" @formname="named-form-handler" Parameter="1" />
<TestComponent method="post" @onsubmit="() => { }" @formname="@("named-form-handler")" Parameter="2" />
@code {
[Parameter] public T Parameter { get; set; }
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_Component_Generic_RazorLangVersion7()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
@typeparam T
<TestComponent method="post" @onsubmit="() => { }" @formname="named-form-handler" Parameter="1" />
<TestComponent method="post" @onsubmit="() => { }" @formname="@("named-form-handler")" Parameter="2" />
@code {
[Parameter] public T Parameter { get; set; }
}
""",
configuration: Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 });
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_Duplicate_HtmlValue()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="x" @formname="y"></form>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_Duplicate_CSharpValue()
{
// This emits invalid code and no warnings, but that's a pre-existing bug,
// happens with the following Razor code, too.
// <input @ref="@a" @ref="@b" />
// @code {
// ElementReference a;
// ElementReference b;
// }
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="@x" @formname="@y"></form>
@code {
string x = "a";
string y = "b";
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_MoreElements_HtmlValue()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="x"></form>
<form method="post" @onsubmit="() => { }" @formname="y"></form>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_MoreElements_CSharpValue()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="@x"></form>
<form method="post" @onsubmit="() => { }" @formname="@y"></form>
@code {
string x = "a";
string y = "b";
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_Nested()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="1"></form>
<TestComponent>
<form method="post" @onsubmit="() => { }" @formname="2"></form>
<TestComponent>
<form method="post" @onsubmit="() => { }" @formname="3"></form>
</TestComponent>
<form method="post" @onsubmit="() => { }" @formname="4"></form>
</TestComponent>
@code {
[Parameter] public RenderFragment ChildContent { get; set; }
}
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9323")]
public void FormName_ChildContent()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form @formname="myform" class="nice">
<p>@DateTime.Now</p>
</form>
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_NoAddNamedEventMethod()
{
// Arrange
var componentsDll = BaseCompilation.References.Single(r => r.Display == "Microsoft.AspNetCore.Components (aspnet80)");
var minimalShim = """
namespace Microsoft.AspNetCore.Components
{
public abstract class ComponentBase
{
protected abstract void BuildRenderTree(Rendering.RenderTreeBuilder __builder);
}
namespace Rendering
{
public sealed class RenderTreeBuilder
{
public void AddMarkupContent(int sequence, string markupContent) { }
public void OpenElement(int sequence, string elementName) { }
public void AddAttribute(int sequence, string name, string value) { }
public void AddAttribute<TArgument>(int sequence, string name, EventCallback<TArgument> value) { }
public void CloseElement() { }
}
}
namespace CompilerServices
{
public static class RuntimeHelpers
{
public static T TypeCheck<T>(T value) => throw null;
}
}
namespace Web
{
[EventHandler("onsubmit", typeof(System.EventArgs), true, true)]
public static class EventHandlers
{
}
}
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class EventHandlerAttribute : System.Attribute
{
public EventHandlerAttribute(string attributeName, System.Type eventArgsType, bool enableStopPropagation, bool enablePreventDefault) { }
}
public readonly struct EventCallback
{
public static readonly EventCallbackFactory Factory;
}
public readonly struct EventCallback<TValue> { }
public sealed class EventCallbackFactory
{
public EventCallback<TValue> Create<TValue>(object receiver, System.Action callback) => throw null;
}
}
""";
var minimalShimRef = CSharpCompilation.Create(
assemblyName: "Microsoft.AspNetCore.Components",
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddSyntaxTrees(Parse(minimalShim))
.AddReferences(ReferenceUtil.NetLatestAll)
.EmitToImageReference();
var baseCompilation = BaseCompilation.ReplaceReference(componentsDll, minimalShimRef);
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="named-form-handler"></form>
""",
baseCompilation: baseCompilation);
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/9077")]
public void FormName_RazorLangVersion7()
{
// Act
var generated = CompileToCSharp("""
@using Microsoft.AspNetCore.Components.Web
<form method="post" @onsubmit="() => { }" @formname="named-form-handler"></form>
""",
configuration: Configuration with { LanguageVersion = RazorLanguageVersion.Version_7_0 });
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact]
public void InjectDirective()
{
// Act
var generated = CompileToCSharp("""
@using System.Text
@inject string Value1
@inject StringBuilder Value2
@inject int Value3;
@inject double Value4
<div>
Content
</div>
@inject float Value5
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11273")]
public void SectionDirective_NotAllowed()
{
// Verify that @section is not recognized in components and produces appropriate code-gen
// Act
var generated = CompileToCSharp("""
@{ var section = "Section"; }
@section One { <p>Content</p> }
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/11273")]
public void SectionDirective_NotAllowed_VariableNotDefined()
{
// Verify that @section is not recognized in components when the variable is not defined
// Act
var generated = CompileToCSharp("""
@section One { <p>Content</p> }
""");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated,
DesignTime
// x:\dir\subdir\Test\TestComponent.cshtml(1,7): error CS0103: The name 'section' does not exist in the current context
? [Diagnostic(ErrorCode.ERR_NameNotInContext, "section").WithArguments("section").WithLocation(1, 7)]
// x:\dir\subdir\Test\TestComponent.cshtml(1,2): error CS0103: The name 'section' does not exist in the current context
: [Diagnostic(ErrorCode.ERR_NameNotInContext, "section").WithArguments("section").WithLocation(1, 2)]);
}
[IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/12663")]
public void ComponentAttribute_WithDoubleAtEscape()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
public class MyComponent : ComponentBase
{
[Parameter]
public string Value { get; set; }
}
}"));
// Act
var generated = CompileToCSharp(@"<MyComponent Value=""@@currentCount"" />");
// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}
#endregion
}
|