aspnetcore/test/Microsoft.AspNetCore.Blazor.../BindRazorIntegrationTest.cs

523 lines
17 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Blazor.Test.Helpers;
using Microsoft.CodeAnalysis.CSharp;
using Xunit;
namespace Microsoft.AspNetCore.Blazor.Build.Test
{
public class BindRazorIntegrationTest : RazorIntegrationTestBase
{
internal override bool UseTwoPhaseCompilation => true;
[Fact]
public void Render_BindToComponent_SpecifiesValue_WithMatchingProperties()
{
// Arrange
AdditionalSyntaxTrees.Add(CSharpSyntaxTree.ParseText(@"
using System;
using Microsoft.AspNetCore.Blazor.Components;
namespace Test
{
public class MyComponent : BlazorComponent
{
public int Value { get; set; }
public Action<int> ValueChanged { get; set; }
}
}"));
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<MyComponent bind-Value=""ParentValue"" />
@functions {
public int ParentValue { get; set; } = 42;
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Component(frame, "Test.MyComponent", 3, 0),
frame => AssertFrame.Attribute(frame, "Value", 42, 1),
frame => AssertFrame.Attribute(frame, "ValueChanged", typeof(Action<int>), 2),
frame => AssertFrame.Whitespace(frame, 3));
}
[Fact]
public void Render_BindToComponent_SpecifiesValue_WithoutMatchingProperties()
{
// Arrange
AdditionalSyntaxTrees.Add(CSharpSyntaxTree.ParseText(@"
using System;
using Microsoft.AspNetCore.Blazor.Components;
namespace Test
{
public class MyComponent : BlazorComponent, IComponent
{
void IComponent.SetParameters(ParameterCollection parameters)
{
}
}
}"));
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<MyComponent bind-Value=""ParentValue"" />
@functions {
public int ParentValue { get; set; } = 42;
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Component(frame, "Test.MyComponent", 3, 0),
frame => AssertFrame.Attribute(frame, "Value", 42, 1),
frame => AssertFrame.Attribute(frame, "ValueChanged", typeof(UIEventHandler), 2),
frame => AssertFrame.Whitespace(frame, 3));
}
[Fact]
public void Render_BindToComponent_SpecifiesValueAndChangeEvent_WithMatchingProperties()
{
// Arrange
AdditionalSyntaxTrees.Add(CSharpSyntaxTree.ParseText(@"
using System;
using Microsoft.AspNetCore.Blazor.Components;
namespace Test
{
public class MyComponent : BlazorComponent
{
public int Value { get; set; }
public Action<int> OnChanged { get; set; }
}
}"));
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<MyComponent bind-Value-OnChanged=""ParentValue"" />
@functions {
public int ParentValue { get; set; } = 42;
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Component(frame, "Test.MyComponent", 3, 0),
frame => AssertFrame.Attribute(frame, "Value", 42, 1),
frame => AssertFrame.Attribute(frame, "OnChanged", typeof(Action<int>), 2),
frame => AssertFrame.Whitespace(frame, 3));
}
[Fact]
public void Render_BindToComponent_SpecifiesValueAndChangeEvent_WithoutMatchingProperties()
{
// Arrange
AdditionalSyntaxTrees.Add(CSharpSyntaxTree.ParseText(@"
using System;
using Microsoft.AspNetCore.Blazor.Components;
namespace Test
{
public class MyComponent : BlazorComponent, IComponent
{
void IComponent.SetParameters(ParameterCollection parameters)
{
}
}
}"));
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<MyComponent bind-Value-OnChanged=""ParentValue"" />
@functions {
public int ParentValue { get; set; } = 42;
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Component(frame, "Test.MyComponent", 3, 0),
frame => AssertFrame.Attribute(frame, "Value", 42, 1),
frame => AssertFrame.Attribute(frame, "OnChanged", typeof(UIEventHandler), 2),
frame => AssertFrame.Whitespace(frame, 3));
}
[Fact]
public void Render_BindToElement_WritesAttributes()
{
// Arrange
AdditionalSyntaxTrees.Add(CSharpSyntaxTree.ParseText(@"
using System;
using Microsoft.AspNetCore.Blazor.Components;
namespace Test
{
[BindElement(""div"", null, ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<div bind=""@ParentValue"" />
@functions {
public string ParentValue { get; set; } = ""hi"";
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Element(frame, "div", 3, 0),
frame => AssertFrame.Attribute(frame, "myvalue", "hi", 1),
frame => AssertFrame.Attribute(frame, "myevent", typeof(UIEventHandler), 2),
frame => AssertFrame.Whitespace(frame, 3));
}
[Fact]
public void Render_BindToElementWithSuffix_WritesAttributes()
{
// Arrange
AdditionalSyntaxTrees.Add(CSharpSyntaxTree.ParseText(@"
using System;
using Microsoft.AspNetCore.Blazor.Components;
namespace Test
{
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<div bind-value=""@ParentValue"" />
@functions {
public string ParentValue { get; set; } = ""hi"";
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Element(frame, "div", 3, 0),
frame => AssertFrame.Attribute(frame, "myvalue", "hi", 1),
frame => AssertFrame.Attribute(frame, "myevent", typeof(UIEventHandler), 2),
frame => AssertFrame.Whitespace(frame, 3));
}
[Fact]
public void Render_BindDuplicates_ReportsDiagnostic()
{
// Arrange
AdditionalSyntaxTrees.Add(CSharpSyntaxTree.ParseText(@"
using System;
using Microsoft.AspNetCore.Blazor.Components;
namespace Test
{
[BindElement(""div"", ""value"", ""myvalue2"", ""myevent2"")]
[BindElement(""div"", ""value"", ""myvalue"", ""myevent"")]
public static class BindAttributes
{
}
}"));
// Act
var result = CompileToCSharp(@"
@addTagHelper *, TestAssembly
<div bind-value=""@ParentValue"" />
@functions {
public string ParentValue { get; set; } = ""hi"";
}");
// Assert
var diagnostic = Assert.Single(result.Diagnostics);
Assert.Equal("BL9989", diagnostic.Id);
Assert.Equal(
"The attribute 'bind-value' was matched by multiple bind attributes. Duplicates:" + Environment.NewLine +
"Test.BindAttributes" + Environment.NewLine +
"Test.BindAttributes",
diagnostic.GetMessage());
}
[Fact]
public void Render_BuiltIn_BindToInputWithoutType_WritesAttributes()
{
// Arrange
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<input bind=""@ParentValue"" />
@functions {
public int ParentValue { get; set; } = 42;
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Element(frame, "input", 3, 0),
frame => AssertFrame.Attribute(frame, "value", "42", 1),
frame => AssertFrame.Attribute(frame, "onchange", typeof(UIEventHandler), 2),
frame => AssertFrame.Whitespace(frame, 3));
}
[Fact]
public void Render_BuiltIn_BindToInputText_WithFormat_WritesAttributes()
{
// Arrange
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<input type=""text"" bind=""@CurrentDate"" format-value=""MM/dd/yyyy""/>
@functions {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Element(frame, "input", 4, 0),
frame => AssertFrame.Attribute(frame, "type", "text", 1),
frame => AssertFrame.Attribute(frame, "value", new DateTime(2018, 1, 1).ToString("MM/dd/yyyy"), 2),
frame => AssertFrame.Attribute(frame, "onchange", typeof(UIEventHandler), 3),
frame => AssertFrame.Whitespace(frame, 4));
}
[Fact]
public void Render_BuiltIn_BindToInputText_WithFormatFromProperty_WritesAttributes()
{
// Arrange
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<input type=""text"" bind=""@CurrentDate"" format-value=""@Format""/>
@functions {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
public string Format { get; set; } = ""MM/dd/yyyy"";
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Element(frame, "input", 4, 0),
frame => AssertFrame.Attribute(frame, "type", "text", 1),
frame => AssertFrame.Attribute(frame, "value", new DateTime(2018, 1, 1).ToString("MM/dd/yyyy"), 2),
frame => AssertFrame.Attribute(frame, "onchange", typeof(UIEventHandler), 3),
frame => AssertFrame.Whitespace(frame, 4));
}
[Fact]
public void Render_BuiltIn_BindToInputText_WritesAttributes()
{
// Arrange
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<input type=""text"" bind=""@ParentValue"" />
@functions {
public int ParentValue { get; set; } = 42;
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Element(frame, "input", 4, 0),
frame => AssertFrame.Attribute(frame, "type", "text", 1),
frame => AssertFrame.Attribute(frame, "value", "42", 2),
frame => AssertFrame.Attribute(frame, "onchange", typeof(UIEventHandler), 3),
frame => AssertFrame.Whitespace(frame, 4));
}
[Fact]
public void Render_BuiltIn_BindToInputCheckbox_WritesAttributes()
{
// Arrange
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<input type=""checkbox"" bind=""@Enabled"" />
@functions {
public bool Enabled { get; set; }
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Element(frame, "input", 3, 0),
frame => AssertFrame.Attribute(frame, "type", "checkbox", 1),
frame => AssertFrame.Attribute(frame, "onchange", typeof(UIEventHandler), 3),
frame => AssertFrame.Whitespace(frame, 4));
}
[Fact]
public void Render_BindToElementFallback_WritesAttributes()
{
// Arrange
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<input type=""text"" bind-value-onchange=""@ParentValue"" />
@functions {
public int ParentValue { get; set; } = 42;
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Element(frame, "input", 4, 0),
frame => AssertFrame.Attribute(frame, "type", "text", 1),
frame => AssertFrame.Attribute(frame, "value", "42", 2),
frame => AssertFrame.Attribute(frame, "onchange", typeof(UIEventHandler), 3),
frame => AssertFrame.Whitespace(frame, 4));
}
[Fact]
public void Render_BindToElementFallback_WithFormat_WritesAttributes()
{
// Arrange
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<input type=""text"" bind-value-onchange=""@CurrentDate"" format-value=""MM/dd"" />
@functions {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Element(frame, "input", 4, 0),
frame => AssertFrame.Attribute(frame, "type", "text", 1),
frame => AssertFrame.Attribute(frame, "value", new DateTime(2018, 1, 1).ToString("MM/dd"), 2),
frame => AssertFrame.Attribute(frame, "onchange", typeof(UIEventHandler), 3),
frame => AssertFrame.Whitespace(frame, 4));
}
[Fact] // Additional coverage of OrphanTagHelperLoweringPass
public void Render_BindToElementFallback_SpecifiesValueAndChangeEvent_WithCSharpAttribute()
{
// Arrange
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<input type=""@(""text"")"" bind-value-onchange=""@ParentValue"" visible />
@functions {
public int ParentValue { get; set; } = 42;
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Element(frame, "input", 5, 0),
frame => AssertFrame.Attribute(frame, "visible", 1), // This gets reordered in the node writer
frame => AssertFrame.Attribute(frame, "type", "text", 2),
frame => AssertFrame.Attribute(frame, "value", "42", 3),
frame => AssertFrame.Attribute(frame, "onchange", typeof(UIEventHandler), 4),
frame => AssertFrame.Whitespace(frame, 5));
}
[Fact] // Additional coverage of OrphanTagHelperLoweringPass
public void Render_BindToElementFallback_SpecifiesValueAndChangeEvent_BodyContent()
{
// Arrange
var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
<div bind-value-onchange=""@ParentValue"">
<span>@(42.ToString())</span>
</div>
@functions {
public int ParentValue { get; set; } = 42;
}");
// Act
var frames = GetRenderTree(component);
// Assert
Assert.Collection(
frames,
frame => AssertFrame.Element(frame, "div", 7, 0),
frame => AssertFrame.Attribute(frame, "value", "42", 1),
frame => AssertFrame.Attribute(frame, "onchange", typeof(UIEventHandler), 2),
frame => AssertFrame.Whitespace(frame, 3),
frame => AssertFrame.Element(frame, "span", 2, 4),
frame => AssertFrame.Text(frame, "42", 5),
frame => AssertFrame.Whitespace(frame, 6),
frame => AssertFrame.Whitespace(frame, 7));
}
[Fact]
public void Render_BindFallback_InvalidSyntax_TooManyParts()
{
// Arrange & Act
var generated = CompileToCSharp(@"
@addTagHelper *, TestAssembly
<input type=""text"" bind-first-second-third=""Text"" />
@functions {
public string Text { get; set; } = ""text"";
}");
// Assert
var diagnostic = Assert.Single(generated.Diagnostics);
Assert.Equal("BL9991", diagnostic.Id);
}
[Fact]
public void Render_BindFallback_InvalidSyntax_TrailingDash()
{
// Arrange & Act
var generated = CompileToCSharp(@"
@addTagHelper *, TestAssembly
<input type=""text"" bind-first-=""Text"" />
@functions {
public string Text { get; set; } = ""text"";
}");
// Assert
var diagnostic = Assert.Single(generated.Diagnostics);
Assert.Equal("BL9991", diagnostic.Id);
}
}
}