Ejemplo n.º 1
0
    public void UpdatesOnlyMatchingAttributeValue()
    {
        // Arrange
        var valuePropName = "testprop";
        var renderer      = new TestRenderer();
        var builder       = new RenderTreeBuilder();

        builder.OpenElement(0, "elem");
        builder.AddAttribute(1, "eventname", (Action)(() => { }));
        builder.SetUpdatesAttributeName(valuePropName);
        builder.AddAttribute(2, valuePropName, "unchanged 1");
        builder.CloseElement();
        builder.OpenElement(3, "elem");
        builder.AddAttribute(4, "eventname", (Action)(() => { }));
        builder.SetUpdatesAttributeName(valuePropName);
        builder.AddAttribute(5, "unrelated prop before", "unchanged 2");
        builder.AddAttribute(6, valuePropName, "initial value");
        builder.AddAttribute(7, "unrelated prop after", "unchanged 3");
        builder.CloseElement();
        var frames = builder.GetFrames();

        frames.Array[1] = frames.Array[1].WithAttributeEventHandlerId(123); // An unrelated event
        frames.Array[4] = frames.Array[4].WithAttributeEventHandlerId(456);

        // Act
        RenderTreeUpdater.UpdateToMatchClientState(builder, 456, "new value");

        // Assert
        Assert.Collection(frames.AsEnumerable(),
                          frame => AssertFrame.Element(frame, "elem", 3, 0),
                          frame => AssertFrame.Attribute(frame, "eventname", v => Assert.IsType <Action>(v), 1),
                          frame => AssertFrame.Attribute(frame, valuePropName, "unchanged 1", 2),
                          frame => AssertFrame.Element(frame, "elem", 5, 3),
                          frame => AssertFrame.Attribute(frame, "eventname", v => Assert.IsType <Action>(v), 4),
                          frame => AssertFrame.Attribute(frame, "unrelated prop before", "unchanged 2", 5),
                          frame => AssertFrame.Attribute(frame, valuePropName, "new value", 6),
                          frame => AssertFrame.Attribute(frame, "unrelated prop after", "unchanged 3", 7));
    }
        public async Task SupportsTwoWayBindingForBoolValues()
        {
            // Arrange/Act
            var component = CompileToComponent(
                @"<input @bind=""MyValue"" />
                @code {
                    public bool MyValue { get; set; } = true;
                }");
            var myValueProperty = component.GetType().GetProperty("MyValue");

            var renderer = new TestRenderer();

            // Assert
            EventCallback setter = default;
            var           frames = GetRenderTree(renderer, component);

            Assert.Collection(frames,
                              frame => AssertFrame.Element(frame, "input", 3, 0),
                              frame => AssertFrame.Attribute(frame, "value", true, 1),
                              frame =>
            {
                AssertFrame.Attribute(frame, "onchange", 2);
                setter = Assert.IsType <EventCallback>(frame.AttributeValue);
            });

            // Trigger the change event to show it updates the property
            //
            // This should always complete synchronously.
            var task = renderer.InvokeAsync(() => setter.InvokeAsync(new UIChangeEventArgs()
            {
                Value = false,
            }));

            Assert.Equal(TaskStatus.RanToCompletion, task.Status);
            await task;

            Assert.False((bool)myValueProperty.GetValue(component));
        }
Ejemplo n.º 3
0
        public void CanNestElements()
        {
            // Arrange
            var builder = new RenderTreeBuilder(new TestRenderer());

            // Act
            builder.AddContent(0, "standalone text 1"); //  0: standalone text 1
            builder.OpenElement(0, "root");             //  1: <root>
            builder.AddContent(0, "root text 1");       //  2:     root text 1
            builder.AddContent(0, "root text 2");       //  3:     root text 2
            builder.OpenElement(0, "child");            //  4:     <child>
            builder.AddContent(0, "child text");        //  5:         child text
            builder.OpenElement(0, "grandchild");       //  6:         <grandchild>
            builder.AddContent(0, "grandchild text 1"); //  7:             grandchild text 1
            builder.AddContent(0, "grandchild text 2"); //  8:             grandchild text 2
            builder.CloseElement();                     //             </grandchild>
            builder.CloseElement();                     //         </child>
            builder.AddContent(0, "root text 3");       //  9:     root text 3
            builder.OpenElement(0, "child 2");          // 10:     <child 2>
            builder.CloseElement();                     //         </child 2>
            builder.CloseElement();                     //      </root>
            builder.AddContent(0, "standalone text 2"); // 11:  standalone text 2

            // Assert
            Assert.Collection(builder.GetFrames().AsEnumerable(),
                              frame => AssertFrame.Text(frame, "standalone text 1"),
                              frame => AssertFrame.Element(frame, "root", 10),
                              frame => AssertFrame.Text(frame, "root text 1"),
                              frame => AssertFrame.Text(frame, "root text 2"),
                              frame => AssertFrame.Element(frame, "child", 5),
                              frame => AssertFrame.Text(frame, "child text"),
                              frame => AssertFrame.Element(frame, "grandchild", 3),
                              frame => AssertFrame.Text(frame, "grandchild text 1"),
                              frame => AssertFrame.Text(frame, "grandchild text 2"),
                              frame => AssertFrame.Text(frame, "root text 3"),
                              frame => AssertFrame.Element(frame, "child 2", 1),
                              frame => AssertFrame.Text(frame, "standalone text 2"));
        }
        public async Task SupportsTwoWayBindingForEnumValues()
        {
            // Arrange/Act
            var myEnumType = FullTypeName <MyEnum>();
            var component  = CompileToComponent(
                $@"<input @bind=""MyValue"" />
                @code {{
                    public {myEnumType} MyValue {{ get; set; }} = {myEnumType}.{nameof(MyEnum.FirstValue)};
                }}");
            var myValueProperty = component.GetType().GetProperty("MyValue");

            var renderer = new TestRenderer();

            // Assert
            EventCallback setter = default;
            var           frames = GetRenderTree(renderer, component);

            Assert.Collection(frames,
                              frame => AssertFrame.Element(frame, "input", 3, 0),
                              frame => AssertFrame.Attribute(frame, "value", MyEnum.FirstValue.ToString(), 1),
                              frame =>
            {
                AssertFrame.Attribute(frame, "onchange", 2);
                setter = Assert.IsType <EventCallback>(frame.AttributeValue);
            });

            // Trigger the change event to show it updates the property
            //
            // This should always complete synchronously.
            var task = renderer.InvokeAsync(() => setter.InvokeAsync(new UIChangeEventArgs {
                Value = MyEnum.SecondValue.ToString(),
            }));

            Assert.Equal(TaskStatus.RanToCompletion, task.Status);
            await task;

            Assert.Equal(MyEnum.SecondValue, (MyEnum)myValueProperty.GetValue(component));
        }
        public async Task SupportsTwoWayBindingForDateValuesWithFormatString()
        {
            // Arrange/Act
            var testDateFormat = "ddd yyyy-MM-dd";
            var component      = CompileToComponent(
                $@"<input @bind=""@MyDate"" @bind:format=""{testDateFormat}"" />
                @code {{
                    public DateTime MyDate {{ get; set; }} = new DateTime(2018, 3, 4);
                }}");
            var myDateProperty = component.GetType().GetProperty("MyDate");

            var renderer = new TestRenderer();

            // Assert
            EventCallback setter = default;
            var           frames = GetRenderTree(renderer, component);

            Assert.Collection(frames,
                              frame => AssertFrame.Element(frame, "input", 3, 0),
                              frame => AssertFrame.Attribute(frame, "value", new DateTime(2018, 3, 4).ToString(testDateFormat), 1),
                              frame =>
            {
                AssertFrame.Attribute(frame, "onchange", 2);
                setter = Assert.IsType <EventCallback>(frame.AttributeValue);
            });

            // Trigger the change event to show it updates the property
            //
            // This should always complete synchronously.
            var task = renderer.InvokeAsync(() => setter.InvokeAsync(new UIChangeEventArgs {
                Value = new DateTime(2018, 3, 5).ToString(testDateFormat),
            }));

            Assert.Equal(TaskStatus.RanToCompletion, task.Status);
            await task;

            Assert.Equal(new DateTime(2018, 3, 5), myDateProperty.GetValue(component));
        }
Ejemplo n.º 6
0
        [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));
        }
        public void Render_AttributeChildContent_IgnoresWhitespaceBody()
        {
            // Arrange
            AdditionalSyntaxTrees.Add(RenderChildContentComponent);

            var component = CompileToComponent(@"
@addTagHelper *, TestAssembly
@{ RenderFragment<string> template = (context) => @<div>@context.ToLowerInvariant()</div>; }
<RenderChildContent ChildContent=""@template(""HI"")"">
       
</RenderChildContent>");

            // Act
            var frames = GetRenderTree(component);

            // Assert
            Assert.Collection(
                frames,
                frame => AssertFrame.Component(frame, "Test.RenderChildContent", 2, 2),
                frame => AssertFrame.Attribute(frame, RenderTreeBuilder.ChildContent, 3),
                frame => AssertFrame.Element(frame, "div", 2, 0),
                frame => AssertFrame.Text(frame, "hi", 1));
        }
        public void Render_BuiltIn_BindToInputText_WithFormatFromProperty_WritesAttributes()
        {
            // Arrange
            var component = CompileToComponent(@"
@using Microsoft.AspNetCore.Components.Web
<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"";
}");

            // 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(EventCallback), 3));
        }
Ejemplo n.º 9
0
        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(Action <UIEventArgs>), 3));
        }
        public async Task SupportsTwoWayBindingForTextareas()
        {
            // Arrange/Act
            var component = CompileToComponent(
                @"<textarea @bind=""MyValue"" ></textarea>
                @code {
                    public string MyValue { get; set; } = ""Initial value"";
                }");
            var myValueProperty = component.GetType().GetProperty("MyValue");

            var renderer = new TestRenderer();

            // Assert
            EventCallback setter = default;
            var           frames = GetRenderTree(renderer, component);

            Assert.Collection(frames,
                              frame => AssertFrame.Element(frame, "textarea", 3, 0),
                              frame => AssertFrame.Attribute(frame, "value", "Initial value", 1),
                              frame =>
            {
                AssertFrame.Attribute(frame, "onchange", 2);
                setter = Assert.IsType <EventCallback>(frame.AttributeValue);
            });

            // Trigger the change event to show it updates the property
            //
            // This should always complete synchronously.
            var task = renderer.InvokeAsync(() => setter.InvokeAsync(new UIChangeEventArgs {
                Value = "Modified value",
            }));

            Assert.Equal(TaskStatus.RanToCompletion, task.Status);
            await task;

            Assert.Equal("Modified value", myValueProperty.GetValue(component));
        }
        public void RazorTemplate_Generic_CanBeUsedFromMethod()
        {
            // Arrange
            var component = CompileToComponent(@"
@(Repeat((context) => @<div>@context.ToLower()</div>, ""Hello, World!"", 3))

@functions {
    RenderFragment Repeat<T>(RenderFragment<T> template, T value, int count)
    {
        return (b) =>
        {
            for (var i = 0; i < count; i++)
            {
                b.AddContent(i, template, value);
            }
        };
    }
}");

            // Act
            var frames = GetRenderTree(component);

            // Assert
            //
            // The sequence numbers start at 1 here because there is an AddContent(0, Repeat(....) call
            // that precedes the definition of the lambda. Sequence numbers for the lambda are allocated
            // from the same logical sequence as the surrounding code.
            Assert.Collection(
                frames,
                frame => AssertFrame.Element(frame, "div", 2, 1),
                frame => AssertFrame.Text(frame, "hello, world!", 2),
                frame => AssertFrame.Element(frame, "div", 2, 1),
                frame => AssertFrame.Text(frame, "hello, world!", 2),
                frame => AssertFrame.Element(frame, "div", 2, 1),
                frame => AssertFrame.Text(frame, "hello, world!", 2));
        }
        public void RazorTemplate_Generic_CanBeUsedFromRazorCode()
        {
            // Arrange
            var component = CompileToComponent(@"
@{ RenderFragment<string> template = (context) => @<div>@context.ToLower()</div>; }
@for (var i = 0; i < 3; i++)
{
    @template(""Hello, World!"");
}
");

            // Act
            var frames = GetRenderTree(component);

            // Assert
            Assert.Collection(
                frames,
                frame => AssertFrame.Element(frame, "div", 2, 0),
                frame => AssertFrame.Text(frame, "hello, world!", 1),
                frame => AssertFrame.Element(frame, "div", 2, 0),
                frame => AssertFrame.Text(frame, "hello, world!", 1),
                frame => AssertFrame.Element(frame, "div", 2, 0),
                frame => AssertFrame.Text(frame, "hello, world!", 1));
        }
Ejemplo n.º 13
0
        [Fact] // See https://github.com/aspnet/Blazor/issues/703
        public void Workaround_703()
        {
            // Arrange
            var component = CompileToComponent(@"
<input bind-value-onchange=""@ParentValue"" type=""text"" visible />
@functions {
    public int ParentValue { get; set; } = 42;
}");

            // Act
            var frames = GetRenderTree(component);

            // Assert
            //
            // The workaround for 703 is that the value attribute MUST be after the type
            // attribute.
            Assert.Collection(
                frames,
                frame => AssertFrame.Element(frame, "input", 5, 0),
                frame => AssertFrame.Attribute(frame, "type", "text", 1),
                frame => AssertFrame.Attribute(frame, "visible", 2),
                frame => AssertFrame.Attribute(frame, "value", "42", 3),
                frame => AssertFrame.Attribute(frame, "onchange", typeof(EventCallback), 4));
        }
Ejemplo n.º 14
0
        public void Render_BodyChildContent_Generic()
        {
            // Arrange
            AdditionalSyntaxTrees.Add(RenderChildContentStringComponent);

            var component = CompileToComponent(@"
<RenderChildContentString Value=""HI"">
  <div>@context.ToLowerInvariant()</div>
</RenderChildContentString>");

            // Act
            var frames = GetRenderTree(component);

            // Assert
            Assert.Collection(
                frames,
                frame => AssertFrame.Component(frame, "Test.RenderChildContentString", 3, 0),
                frame => AssertFrame.Attribute(frame, "Value", "HI", 1),
                frame => AssertFrame.Attribute(frame, "ChildContent", 2),
                frame => AssertFrame.MarkupWhitespace(frame, 3),
                frame => AssertFrame.Element(frame, "div", 2, 4),
                frame => AssertFrame.Text(frame, "hi", 5),
                frame => AssertFrame.MarkupWhitespace(frame, 6));
        }
Ejemplo n.º 15
0
        public void CanAddMultipleReferenceCapturesToSameElement()
        {
            // There won't be any way of doing this from Razor because there's no known use
            // case for it. However it's harder to *not* support it than to support it, and
            // there's no known reason to prevent it, so here's test coverage to show it
            // just works.

            // Arrange
            var builder = new RenderTreeBuilder(new TestRenderer());
            Action <ElementRef> referenceCaptureAction1 = elementRef => { };
            Action <ElementRef> referenceCaptureAction2 = elementRef => { };

            // Act
            builder.OpenElement(0, "myelement");
            builder.AddElementReferenceCapture(0, referenceCaptureAction1);
            builder.AddElementReferenceCapture(0, referenceCaptureAction2);
            builder.CloseElement();

            // Assert
            Assert.Collection(builder.GetFrames(),
                              frame => AssertFrame.Element(frame, "myelement", 3),
                              frame => AssertFrame.ElementReferenceCapture(frame, referenceCaptureAction1),
                              frame => AssertFrame.ElementReferenceCapture(frame, referenceCaptureAction2));
        }
Ejemplo n.º 16
0
        public void CanIncludeChildrenInComponents()
        {
            // Arrange/Act
            var testComponentTypeName = FullTypeName<TestComponent>();
            var component = CompileToComponent($"<c:{testComponentTypeName} MyAttr=@(\"abc\")>" +
                $"Some text" +
                $"<some-child a='1'>Nested text</some-child>" +
                $"</c:{testComponentTypeName}>");
            var frames = GetRenderTree(component);

            // Assert: component frames are correct
            Assert.Collection(frames,
                frame => AssertFrame.Component<TestComponent>(frame, 3, 0),
                frame => AssertFrame.Attribute(frame, "MyAttr", "abc", 1),
                frame => AssertFrame.Attribute(frame, RenderTreeBuilder.ChildContent, 2));

            // Assert: Captured ChildContent frames are correct
            var childFrames = GetFrames((RenderFragment)frames[2].AttributeValue);
            Assert.Collection(childFrames,
                frame => AssertFrame.Text(frame, "Some text", 3),
                frame => AssertFrame.Element(frame, "some-child", 3, 4),
                frame => AssertFrame.Attribute(frame, "a", "1", 5),
                frame => AssertFrame.Text(frame, "Nested text", 6));
        }
Ejemplo n.º 17
0
        public void OmitsAttributeIfNotFoundButValueIsOmissible()
        {
            // Arrange
            var valuePropName = "testprop";
            var renderer      = new TestRenderer();
            var builder       = new RenderTreeBuilder();

            builder.OpenElement(0, "elem");
            builder.AddAttribute(1, "eventname", (Action)(() => { }));
            builder.SetUpdatesAttributeName(valuePropName);
            builder.CloseElement();
            var frames = builder.GetFrames();

            frames.Array[1] = frames.Array[1].WithAttributeEventHandlerId(123);

            // Act
            RenderTreeUpdater.UpdateToMatchClientState(builder, 123, false);
            frames = builder.GetFrames();

            // Assert
            Assert.Collection(frames.AsEnumerable(),
                              frame => AssertFrame.Element(frame, "elem", 2, 0),
                              frame => AssertFrame.Attribute(frame, "eventname", v => Assert.IsType <Action>(v), 1));
        }
Ejemplo n.º 18
0
        public void CanAddKeyToElement()
        {
            // Arrange
            var builder  = new RenderTreeBuilder(new TestRenderer());
            var keyValue = new object();

            // Act
            builder.OpenElement(0, "elem");
            builder.AddAttribute(1, "attribute before", "before value");
            builder.SetKey(keyValue);
            builder.AddAttribute(2, "attribute after", "after value");
            builder.CloseElement();

            // Assert
            Assert.Collection(
                builder.GetFrames().AsEnumerable(),
                frame =>
            {
                AssertFrame.Element(frame, "elem", 3, 0);
                Assert.Same(keyValue, frame.ElementKey);
            },
                frame => AssertFrame.Attribute(frame, "attribute before", "before value", 1),
                frame => AssertFrame.Attribute(frame, "attribute after", "after value", 2));
        }
Ejemplo n.º 19
0
        public void BuildRenderTree_ForPropertyWithDisplayAttributeAndStringLocalizer_Should_CreateCorrectFramesUsingStringLocalizer()
        {
            // Arrange
            var stringLocalizerMock = new Mock <IStringLocalizer>();

            stringLocalizerMock.Setup(l => l[It.IsAny <string>()]).Returns(new LocalizedString("name", "value"));

            _sut.StringLocalizerMock = stringLocalizerMock.Object;
            _sut.For = () => _model.First;

            // Act
            var renderTreeBuilder = new RenderTreeBuilder();

            _sut.BuildRenderTreeMock(renderTreeBuilder);

            // Assert
            var frames = renderTreeBuilder.GetFrames().Array;

            AssertFrame.Element(frames[0], "label", 2);
            AssertFrame.Text(frames[1], "value");

            // Verify
            stringLocalizerMock.Verify(localizer => localizer["Person_First"], Times.Once);
        }
Ejemplo n.º 20
0
        public void Render_HtmlBlock_Integration()
        {
            // Arrange
            AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
    public class MyComponent : ComponentBase
    {
        [Parameter]
        public RenderFragment ChildContent { get; set; }
    }
}
"));

            var component = CompileToComponent(@"
<html>
  <head><meta><meta></head>
  <body>
    <MyComponent>
      <div><span></span><span></span></div>
      <div>@(""hi"")</div>
      <div><span></span><span></span></div>
      <div></div>
      <div>@(""hi"")</div>
      <div></div>
  </MyComponent>
  </body>
</html>");

            // Act
            var frames = GetRenderTree(component);

            // Assert: component frames are correct
            Assert.Collection(
                frames,
                frame => AssertFrame.Element(frame, "html", 9, 0),
                frame => AssertFrame.MarkupWhitespace(frame, 1),
                frame => AssertFrame.Markup(frame, "<head><meta><meta></head>\n  ", 2),
                frame => AssertFrame.Element(frame, "body", 5, 3),
                frame => AssertFrame.MarkupWhitespace(frame, 4),
                frame => AssertFrame.Component(frame, "Test.MyComponent", 2, 5),
                frame => AssertFrame.Attribute(frame, "ChildContent", 6),
                frame => AssertFrame.MarkupWhitespace(frame, 16),
                frame => AssertFrame.MarkupWhitespace(frame, 17));

            // Assert: Captured ChildContent frames are correct
            var childFrames = GetFrames((RenderFragment)frames[6].AttributeValue);

            Assert.Collection(
                childFrames.AsEnumerable(),
                frame => AssertFrame.MarkupWhitespace(frame, 7),
                frame => AssertFrame.Markup(frame, "<div><span></span><span></span></div>\n      ", 8),
                frame => AssertFrame.Element(frame, "div", 2, 9),
                frame => AssertFrame.Text(frame, "hi", 10),
                frame => AssertFrame.MarkupWhitespace(frame, 11),
                frame => AssertFrame.Markup(frame, "<div><span></span><span></span></div>\n      <div></div>\n      ", 12),
                frame => AssertFrame.Element(frame, "div", 2, 13),
                frame => AssertFrame.Text(frame, "hi", 14),
                frame => AssertFrame.Markup(frame, "\n      <div></div>\n  ", 15));
        }