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); }
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); }
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); }
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); }
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); }
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"); }
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); }); }
public void CannotDispatchEventsToUnknownComponents() { // Arrange var renderer = new TestRenderer(); // Act/Assert Assert.Throws <ArgumentException>(() => { renderer.DispatchEvent(123, 0, new UIEventArgs()); }); }
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); }
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); }
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); }
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"); }); }); }