// 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 ValueChanged { get; set; } } }")); var component = CompileToComponent(@" @addTagHelper *, TestAssembly @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), 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 @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 OnChanged { get; set; } } }")); var component = CompileToComponent(@" @addTagHelper *, TestAssembly @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), 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 @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
@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
@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
@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 @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 @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 @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 @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 @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 @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 @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 @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
@(42.ToString())
@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_Bind_With_IncorrectAttributeNames() { //more than 3 parts Assert.Throws(() => CompileToComponent(@" @addTagHelper *, TestAssembly @functions { public string Text { get; set; } = ""text""; }")); //ends with '-' Assert.Throws(() => CompileToComponent(@" @addTagHelper *, TestAssembly @functions { public string Text { get; set; } = ""text""; }")); } } }