Example #1
0
        public void DisposesEventHandlersWhenAncestorElementRemoved()
        {
            // Arrange
            var            renderer         = new TestRenderer();
            var            eventCount       = 0;
            UIEventHandler origEventHandler = args => { eventCount++; };
            var            component        = new EventComponent {
                OnTest = origEventHandler
            };
            var componentId = renderer.AssignComponentId(component);

            component.TriggerRender();
            var origEventHandlerId = renderer.Batches.Single()
                                     .ReferenceFrames
                                     .Where(f => f.FrameType == RenderTreeFrameType.Attribute)
                                     .Single(f => f.AttributeEventHandlerId != 0)
                                     .AttributeEventHandlerId;

            // Act/Assert 1: Event handler fires when we trigger it
            Assert.Equal(0, eventCount);
            renderer.DispatchEvent(componentId, origEventHandlerId, args: null);
            Assert.Equal(1, eventCount);

            // Now remove the ancestor element
            component.SkipElement = true;
            component.TriggerRender();

            // Act/Assert 2: Can no longer fire the original event
            Assert.Throws <ArgumentException>(() =>
            {
                renderer.DispatchEvent(componentId, origEventHandlerId, args: null);
            });
            Assert.Equal(1, eventCount);
        }
Example #2
0
        public void DisposesEventHandlersWhenOwnerComponentRemoved()
        {
            // Arrange
            var            renderer         = new TestRenderer();
            var            eventCount       = 0;
            UIEventHandler origEventHandler = args => { eventCount++; };
            var            component        = new ConditionalParentComponent <EventComponent>
            {
                IncludeChild    = true,
                ChildParameters = new Dictionary <string, object>
                {
                    { nameof(EventComponent.OnTest), origEventHandler }
                }
            };
            var rootComponentId = renderer.AssignComponentId(component);

            component.TriggerRender();
            var batch               = renderer.Batches.Single();
            var rootComponentDiff   = batch.DiffsByComponentId[rootComponentId].Single();
            var rootComponentFrame  = batch.ReferenceFrames[0];
            var childComponentFrame = rootComponentDiff.Edits
                                      .Select(e => batch.ReferenceFrames[e.ReferenceFrameIndex])
                                      .Where(f => f.FrameType == RenderTreeFrameType.Component)
                                      .Single();
            var childComponentId   = childComponentFrame.ComponentId;
            var childComponentDiff = batch.DiffsByComponentId[childComponentFrame.ComponentId].Single();
            var eventHandlerId     = batch.ReferenceFrames
                                     .Skip(childComponentDiff.Edits[0].ReferenceFrameIndex) // Search from where the child component frames start
                                     .Where(f => f.FrameType == RenderTreeFrameType.Attribute)
                                     .Single(f => f.AttributeEventHandlerId != 0)
                                     .AttributeEventHandlerId;

            // Act/Assert 1: Event handler fires when we trigger it
            Assert.Equal(0, eventCount);
            renderer.DispatchEvent(childComponentId, eventHandlerId, args: null);
            Assert.Equal(1, eventCount);

            // Now remove the EventComponent
            component.IncludeChild = false;
            component.TriggerRender();

            // Act/Assert 2: Can no longer fire the original event
            Assert.Throws <ArgumentException>(() =>
            {
                renderer.DispatchEvent(eventHandlerId, eventHandlerId, args: null);
            });
            Assert.Equal(1, eventCount);
        }
Example #3
0
        public void CanDispatchEventsToTopLevelComponents()
        {
            // Arrange: Render a component with an event handler
            var         renderer     = new TestRenderer();
            UIEventArgs receivedArgs = null;

            var component = new EventComponent
            {
                Handler = args => { receivedArgs = args; }
            };
            var componentId = renderer.AssignComponentId(component);

            renderer.RenderNewBatch(componentId);

            var(eventHandlerFrameIndex, _) = FirstWithIndex(
                renderer.Batches.Single().DiffsByComponentId[componentId].Single().ReferenceFrames,
                frame => frame.AttributeValue != null);

            // Assert: Event not yet fired
            Assert.Null(receivedArgs);

            // Act/Assert: Event can be fired
            var eventArgs = new UIEventArgs();

            renderer.DispatchEvent(componentId, eventHandlerFrameIndex, eventArgs);
            Assert.Same(eventArgs, receivedArgs);
        }
Example #4
0
        public void ThrowsIfComponentDoesNotHandleEvents()
        {
            // Arrange: Render a component with an event handler
            var            renderer  = new TestRenderer();
            UIEventHandler handler   = args => throw new NotImplementedException();
            var            component = new TestComponent(builder =>
            {
                builder.OpenElement(0, "mybutton");
                builder.AddAttribute(1, "onclick", handler);
                builder.CloseElement();
            });

            var componentId = renderer.AssignComponentId(component);

            component.TriggerRender();

            var eventHandlerId = renderer.Batches.Single()
                                 .ReferenceFrames
                                 .First(frame => frame.AttributeValue != null)
                                 .AttributeEventHandlerId;
            var eventArgs = new UIEventArgs();

            // Act/Assert
            var ex = Assert.Throws <InvalidOperationException>(() =>
            {
                renderer.DispatchEvent(componentId, eventHandlerId, eventArgs);
            });

            Assert.Equal($"The component of type {typeof(TestComponent).FullName} cannot receive " +
                         $"events because it does not implement {typeof(IHandleEvent).FullName}.", ex.Message);
        }
Example #5
0
        public void CanDispatchActionEventsToTopLevelComponents()
        {
            // Arrange: Render a component with an event handler
            var    renderer     = new TestRenderer();
            object receivedArgs = null;

            var component = new EventComponent
            {
                OnClickAction = () => { receivedArgs = new object(); }
            };
            var componentId = renderer.AssignComponentId(component);

            component.TriggerRender();

            var eventHandlerId = renderer.Batches.Single()
                                 .ReferenceFrames
                                 .First(frame => frame.AttributeValue != null)
                                 .AttributeEventHandlerId;

            // Assert: Event not yet fired
            Assert.Null(receivedArgs);

            // Act/Assert: Event can be fired
            var eventArgs = new UIMouseEventArgs();

            renderer.DispatchEvent(componentId, eventHandlerId, eventArgs);
            Assert.NotNull(receivedArgs);
        }
Example #6
0
        public void CanCombineBindAndConditionalAttribute()
        {
            // This test represents https://github.com/aspnet/Blazor/issues/624

            // Arrange: Rendered with textbox enabled
            var renderer    = new TestRenderer();
            var component   = new BindPlusConditionalAttributeComponent();
            var componentId = renderer.AssignComponentId(component);

            component.TriggerRender();
            var checkboxChangeEventHandlerId = renderer.Batches.Single()
                                               .ReferenceFrames
                                               .First(frame => frame.FrameType == RenderTreeFrameType.Attribute && frame.AttributeEventHandlerId != 0)
                                               .AttributeEventHandlerId;

            // Act: Toggle the checkbox
            var eventArgs = new UIChangeEventArgs {
                Value = true
            };

            renderer.DispatchEvent(componentId, checkboxChangeEventHandlerId, eventArgs);
            var latestBatch     = renderer.Batches.Last();
            var latestDiff      = latestBatch.DiffsInOrder.Single();
            var referenceFrames = latestBatch.ReferenceFrames;

            // Assert: Textbox's "disabled" attribute was removed
            Assert.Equal(2, renderer.Batches.Count);
            Assert.Equal(componentId, latestDiff.ComponentId);
            Assert.Contains(latestDiff.Edits, edit =>
                            edit.SiblingIndex == 1 &&
                            edit.RemovedAttributeName == "disabled");
        }
Example #7
0
        public void QueuedRenderIsSkippedIfComponentWasAlreadyDisposedInSameBatch()
        {
            // Arrange
            var           renderer          = new TestRenderer();
            var           shouldRenderChild = true;
            TestComponent component         = null;

            component = new TestComponent(builder =>
            {
                builder.AddContent(0, "Some frame so the child isn't at position zero");
                if (shouldRenderChild)
                {
                    builder.OpenComponent <RendersSelfAfterEventComponent>(1);
                    builder.AddAttribute(2, "onclick", (Action <object>)((object obj) =>
                    {
                        // First we queue (1) a re-render of the root component, then the child component
                        // will queue (2) its own re-render. But by the time (1) completes, the child will
                        // have been disposed, even though (2) is still in the queue
                        shouldRenderChild = false;
                        component.TriggerRender();
                    }));
                    builder.CloseComponent();
                }
            });

            var componentId = renderer.AssignComponentId(component);

            component.TriggerRender();
            var childComponentId = renderer.Batches.Single()
                                   .ReferenceFrames
                                   .Where(f => f.ComponentId != 0)
                                   .Single()
                                   .ComponentId;
            var origEventHandlerId = renderer.Batches.Single()
                                     .ReferenceFrames
                                     .Where(f => f.FrameType == RenderTreeFrameType.Attribute && f.AttributeName == "onclick")
                                     .Single(f => f.AttributeEventHandlerId != 0)
                                     .AttributeEventHandlerId;

            // Act
            // The fact that there's no error here is the main thing we're testing
            renderer.DispatchEvent(childComponentId, origEventHandlerId, args: null);

            // Assert: correct render result
            var newBatch = renderer.Batches.Skip(1).Single();

            Assert.Equal(1, newBatch.DisposedComponentIDs.Count);
            Assert.Equal(1, newBatch.DiffsByComponentId.Count);
            Assert.Collection(newBatch.DiffsByComponentId[componentId].Single().Edits,
                              edit =>
            {
                Assert.Equal(RenderTreeEditType.RemoveFrame, edit.Type);
                Assert.Equal(1, edit.SiblingIndex);
            });
        }
Example #8
0
        public void CannotDispatchEventsToUnknownComponents()
        {
            // Arrange
            var renderer = new TestRenderer();

            // Act/Assert
            Assert.Throws <ArgumentException>(() =>
            {
                renderer.DispatchEvent(123, 0, new UIEventArgs());
            });
        }
Example #9
0
        public void DisposesEventHandlersWhenAttributeValueChanged()
        {
            // Arrange
            var            renderer         = new TestRenderer();
            var            eventCount       = 0;
            UIEventHandler origEventHandler = args => { eventCount++; };
            var            component        = new EventComponent {
                Handler = origEventHandler
            };
            var componentId = renderer.AssignComponentId(component);

            renderer.RenderNewBatch(componentId);
            var origEventHandlerId = renderer.Batches.Single()
                                     .ReferenceFrames
                                     .Where(f => f.FrameType == RenderTreeFrameType.Attribute)
                                     .Single(f => f.AttributeEventHandlerId != 0)
                                     .AttributeEventHandlerId;

            // Act/Assert 1: Event handler fires when we trigger it
            Assert.Equal(0, eventCount);
            renderer.DispatchEvent(componentId, origEventHandlerId, args: null);
            Assert.Equal(1, eventCount);

            // Now change the attribute value
            var newEventCount = 0;

            component.Handler = args => { newEventCount++; };
            renderer.RenderNewBatch(componentId);

            // Act/Assert 2: Can no longer fire the original event, but can fire the new event
            Assert.Throws <ArgumentException>(() =>
            {
                renderer.DispatchEvent(componentId, origEventHandlerId, args: null);
            });
            Assert.Equal(1, eventCount);
            Assert.Equal(0, newEventCount);
            renderer.DispatchEvent(componentId, origEventHandlerId + 1, args: null);
            Assert.Equal(1, newEventCount);
        }
Example #10
0
        public void CanDispatchEventsToNestedComponents()
        {
            UIEventArgs receivedArgs = null;

            // Arrange: Render parent component
            var renderer        = new TestRenderer();
            var parentComponent = new TestComponent(builder =>
            {
                builder.OpenComponent <EventComponent>(0);
                builder.CloseComponent();
            });
            var parentComponentId = renderer.AssignComponentId(parentComponent);

            renderer.RenderNewBatch(parentComponentId);

            // Arrange: Render nested component
            var nestedComponentFrame = renderer.Batches.Single().DiffsByComponentId[parentComponentId]
                                       .Single()
                                       .ReferenceFrames
                                       .Single(frame => frame.FrameType == RenderTreeFrameType.Component);
            var nestedComponent = (EventComponent)nestedComponentFrame.Component;

            nestedComponent.Handler = args => { receivedArgs = args; };
            var nestedComponentId = nestedComponentFrame.ComponentId;

            renderer.RenderNewBatch(nestedComponentId);

            // Find nested component's event handler ndoe
            var(eventHandlerFrameIndex, _) = FirstWithIndex(
                renderer.Batches[1].DiffsByComponentId[nestedComponentId].Single().ReferenceFrames,
                frame => frame.AttributeValue != null);

            // Assert: Event not yet fired
            Assert.Null(receivedArgs);

            // Act/Assert: Event can be fired
            var eventArgs = new UIEventArgs();

            renderer.DispatchEvent(nestedComponentId, eventHandlerFrameIndex, eventArgs);
            Assert.Same(eventArgs, receivedArgs);
        }
Example #11
0
        public void CanDispatchEventsToNestedComponents()
        {
            UIEventArgs receivedArgs = null;

            // Arrange: Render parent component
            var renderer        = new TestRenderer();
            var parentComponent = new TestComponent(builder =>
            {
                builder.OpenComponent <EventComponent>(0);
                builder.CloseComponent();
            });
            var parentComponentId = renderer.AssignComponentId(parentComponent);

            parentComponent.TriggerRender();

            // Arrange: Render nested component
            var nestedComponentFrame = renderer.Batches.Single()
                                       .ReferenceFrames
                                       .Single(frame => frame.FrameType == RenderTreeFrameType.Component);
            var nestedComponent = (EventComponent)nestedComponentFrame.Component;

            nestedComponent.OnTest = args => { receivedArgs = args; };
            var nestedComponentId = nestedComponentFrame.ComponentId;

            nestedComponent.TriggerRender();

            // Find nested component's event handler ID
            var eventHandlerId = renderer.Batches[1]
                                 .ReferenceFrames
                                 .First(frame => frame.AttributeValue != null)
                                 .AttributeEventHandlerId;

            // Assert: Event not yet fired
            Assert.Null(receivedArgs);

            // Act/Assert: Event can be fired
            var eventArgs = new UIEventArgs();

            renderer.DispatchEvent(nestedComponentId, eventHandlerId, eventArgs);
            Assert.Same(eventArgs, receivedArgs);
        }
Example #12
0
        public void AllRendersTriggeredSynchronouslyDuringEventHandlerAreHandledAsSingleBatch()
        {
            // Arrange: A root component with a child whose event handler explicitly queues
            // a re-render of both the root component and the child
            var            renderer       = new TestRenderer();
            var            eventCount     = 0;
            TestComponent  rootComponent  = null;
            EventComponent childComponent = null;

            rootComponent = new TestComponent(builder =>
            {
                builder.AddContent(0, "Child event count: " + eventCount);
                builder.OpenComponent <EventComponent>(1);
                builder.AddAttribute(2, nameof(EventComponent.OnTest), args =>
                {
                    eventCount++;
                    rootComponent.TriggerRender();
                    childComponent.TriggerRender();
                });
                builder.CloseComponent();
            });
            var rootComponentId = renderer.AssignComponentId(rootComponent);

            rootComponent.TriggerRender();
            var origBatchReferenceFrames = renderer.Batches.Single().ReferenceFrames;
            var childComponentFrame      = origBatchReferenceFrames
                                           .Single(f => f.Component is EventComponent);
            var childComponentId = childComponentFrame.ComponentId;

            childComponent = (EventComponent)childComponentFrame.Component;
            var origEventHandlerId = origBatchReferenceFrames
                                     .Where(f => f.FrameType == RenderTreeFrameType.Attribute)
                                     .Last(f => f.AttributeEventHandlerId != 0)
                                     .AttributeEventHandlerId;

            Assert.Single(renderer.Batches);

            // Act
            renderer.DispatchEvent(childComponentId, origEventHandlerId, args: null);

            // Assert
            Assert.Equal(2, renderer.Batches.Count);
            var batch = renderer.Batches.Last();

            Assert.Collection(batch.DiffsInOrder,
                              diff =>
            {
                // First we triggered the root component to re-render
                Assert.Equal(rootComponentId, diff.ComponentId);
                Assert.Collection(diff.Edits, edit =>
                {
                    Assert.Equal(RenderTreeEditType.UpdateText, edit.Type);
                    AssertFrame.Text(
                        batch.ReferenceFrames[edit.ReferenceFrameIndex],
                        "Child event count: 1");
                });
            },
                              diff =>
            {
                // Then the root re-render will have triggered an update to the child
                Assert.Equal(childComponentId, diff.ComponentId);
                Assert.Collection(diff.Edits, edit =>
                {
                    Assert.Equal(RenderTreeEditType.UpdateText, edit.Type);
                    AssertFrame.Text(
                        batch.ReferenceFrames[edit.ReferenceFrameIndex],
                        "Render count: 2");
                });
            },
                              diff =>
            {
                // Finally we explicitly requested a re-render of the child
                Assert.Equal(childComponentId, diff.ComponentId);
                Assert.Collection(diff.Edits, edit =>
                {
                    Assert.Equal(RenderTreeEditType.UpdateText, edit.Type);
                    AssertFrame.Text(
                        batch.ReferenceFrames[edit.ReferenceFrameIndex],
                        "Render count: 3");
                });
            });
        }