public async Task SupportsTwoWayBindingForEnumValues()
        {
            // Arrange/Act
            var myEnumType = FullTypeName <MyEnum>();
            var component  = CompileToComponent(
                $@"<input bind=""MyValue"" />
                @functions {{
                    public {myEnumType} MyValue {{ get; set; }} = {myEnumType}.{nameof(MyEnum.FirstValue)};
                }}");
            var myValueProperty = component.GetType().GetProperty("MyValue");

            var renderer = new TestRenderer();

            // Assert
            Action <UIEventArgs> setter = null;
            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 <Action <UIEventArgs> >(frame.AttributeValue);
            });

            // Trigger the change event to show it updates the property
            await renderer.Invoke(() => setter(new UIChangeEventArgs()
            {
                Value = MyEnum.SecondValue.ToString(),
            }));

            Assert.Equal(MyEnum.SecondValue, (MyEnum)myValueProperty.GetValue(component));
        }
        public async Task SupportsTwoWayBindingForBoolValues()
        {
            // Arrange/Act
            var component = CompileToComponent(
                @"<input bind=""MyValue"" />
                @functions {
                    public bool MyValue { get; set; } = true;
                }");
            var myValueProperty = component.GetType().GetProperty("MyValue");

            var renderer = new TestRenderer();

            // Assert
            Action <UIEventArgs> setter = null;
            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 <Action <UIEventArgs> >(frame.AttributeValue);
            });

            // Trigger the change event to show it updates the property
            await renderer.Invoke(() => setter(new UIChangeEventArgs()
            {
                Value = false,
            }));

            Assert.False((bool)myValueProperty.GetValue(component));
        }
        public async Task SupportsTwoWayBindingForTextareas()
        {
            // Arrange/Act
            var component = CompileToComponent(
                @"<textarea bind=""MyValue"" ></textarea>
                @functions {
                    public string MyValue { get; set; } = ""Initial value"";
                }");
            var myValueProperty = component.GetType().GetProperty("MyValue");

            var renderer = new TestRenderer();

            // Assert
            Action <UIEventArgs> setter = null;
            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 <Action <UIEventArgs> >(frame.AttributeValue);
            });

            // Trigger the change event to show it updates the property
            await renderer.Invoke(() => setter(new UIChangeEventArgs()
            {
                Value = "Modified value",
            }));

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

            var renderer = new TestRenderer();

            // Assert
            Action <UIEventArgs> setter = null;
            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 <Action <UIEventArgs> >(frame.AttributeValue);
            });

            // Trigger the change event to show it updates the property
            await renderer.Invoke(() => setter(new UIChangeEventArgs()
            {
                Value = new DateTime(2018, 3, 5).ToString(testDateFormat),
            }));

            Assert.Equal(new DateTime(2018, 3, 5), myDateProperty.GetValue(component));
        }
        static CascadingValue <T> CreateCascadingValueComponent <T>(T value, string name = null)
        {
            var supplier = new CascadingValue <T>();
            var renderer = new TestRenderer();

            supplier.Configure(new RenderHandle(renderer, 0));

            var supplierParams = new Dictionary <string, object>
            {
                { "Value", value }
            };

            if (name != null)
            {
                supplierParams.Add("Name", name);
            }

            renderer.Invoke(() => supplier.SetParametersAsync(ParameterCollection.FromDictionary(supplierParams)));
            return(supplier);
        }