public void DisposesEventHandlersWhenAncestorElementRemoved() { // 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 remove the ancestor element component.SkipElement = true; renderer.RenderNewBatch(componentId); // 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 ReRendersChildComponentsWhenPropertiesChange() { // Arrange: First render var renderer = new TestRenderer(); var firstRender = true; var component = new TestComponent(builder => { builder.OpenComponent <MessageComponent>(1); builder.AddAttribute(2, nameof(MessageComponent.Message), firstRender ? "first" : "second"); builder.CloseComponent(); }); var rootComponentId = renderer.AssignComponentId(component); renderer.RenderNewBatch(rootComponentId); var childComponentId = renderer.Batches.Single() .ReferenceFrames .Single(frame => frame.FrameType == RenderTreeFrameType.Component) .ComponentId; // Act: Second render firstRender = false; renderer.RenderNewBatch(rootComponentId); var diff = renderer.Batches[1].DiffsByComponentId[childComponentId].Single(); // Assert Assert.Collection(diff.Edits, edit => { Assert.Equal(RenderTreeEditType.UpdateText, edit.Type); Assert.Equal(0, edit.ReferenceFrameIndex); }); AssertFrame.Text(renderer.Batches[1].ReferenceFrames[0], "second"); }
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.Handler), origEventHandler } } }; var rootComponentId = renderer.AssignComponentId(component); renderer.RenderNewBatch(rootComponentId); 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; renderer.RenderNewBatch(rootComponentId); // 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 CanRenderTopLevelComponents() { // Arrange var renderer = new TestRenderer(); var component = new TestComponent(builder => { builder.OpenElement(0, "my element"); builder.AddText(1, "some text"); builder.CloseElement(); }); // Act var componentId = renderer.AssignComponentId(component); renderer.RenderNewBatch(componentId); // Assert var diff = renderer.Batches.Single().DiffsByComponentId[componentId].Single(); Assert.Collection(diff.Edits, edit => { Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); Assert.Equal(0, edit.ReferenceFrameIndex); }); Assert.Collection(diff.ReferenceFrames, frame => AssertFrame.Element(frame, "my element", 2), frame => AssertFrame.Text(frame, "some text")); }
public void CanRenderNestedComponents() { // Arrange var renderer = new TestRenderer(); var component = new TestComponent(builder => { builder.AddText(0, "Hello"); builder.OpenComponent <MessageComponent>(1); builder.AddAttribute(2, nameof(MessageComponent.Message), "Nested component output"); builder.CloseComponent(); }); // Act/Assert var componentId = renderer.AssignComponentId(component); renderer.RenderNewBatch(componentId); var batch = renderer.Batches.Single(); var componentFrame = batch.DiffsByComponentId[componentId].Single().ReferenceFrames .Single(frame => frame.FrameType == RenderTreeFrameType.Component); var nestedComponentId = componentFrame.ComponentId; var nestedComponentDiff = batch.DiffsByComponentId[nestedComponentId].Single(); // The nested component exists Assert.IsType <MessageComponent>(componentFrame.Component); // The nested component was rendered as part of the batch Assert.Collection(nestedComponentDiff.Edits, edit => { Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); Assert.Equal(0, edit.ReferenceFrameIndex); }); Assert.Collection(nestedComponentDiff.ReferenceFrames, frame => AssertFrame.Text(frame, "Nested component output")); }
public void CanReRenderNestedComponents() { // Arrange: parent component already rendered var renderer = new TestRenderer(); var parentComponent = new TestComponent(builder => { builder.OpenComponent <MessageComponent>(0); builder.CloseComponent(); }); var parentComponentId = renderer.AssignComponentId(parentComponent); renderer.RenderNewBatch(parentComponentId); var nestedComponentFrame = renderer.Batches.Single().DiffsByComponentId[parentComponentId] .Single() .ReferenceFrames .Single(frame => frame.FrameType == RenderTreeFrameType.Component); var nestedComponent = (MessageComponent)nestedComponentFrame.Component; var nestedComponentId = nestedComponentFrame.ComponentId; // Assert: inital render nestedComponent.Message = "Render 1"; renderer.RenderNewBatch(nestedComponentId); var firstDiff = renderer.Batches[1].DiffsByComponentId[nestedComponentId].Single(); Assert.Collection(firstDiff.Edits, edit => { Assert.Equal(RenderTreeEditType.UpdateText, edit.Type); Assert.Equal(0, edit.ReferenceFrameIndex); }); Assert.Collection(firstDiff.ReferenceFrames, frame => AssertFrame.Text(frame, "Render 1")); // Act/Assert: re-render nestedComponent.Message = "Render 2"; renderer.RenderNewBatch(nestedComponentId); var secondDiff = renderer.Batches[2].DiffsByComponentId[nestedComponentId].Single(); Assert.Collection(firstDiff.Edits, edit => { Assert.Equal(RenderTreeEditType.UpdateText, edit.Type); Assert.Equal(0, edit.ReferenceFrameIndex); }); Assert.Collection(firstDiff.ReferenceFrames, frame => AssertFrame.Text(frame, "Render 2")); }
public void NotifiesIHandlePropertiesChangedWhenChanged() { // Arrange var renderer = new TestRenderer(); var component = new ConditionalParentComponent <HandlePropertiesChangedComponent> { IncludeChild = true, ChildParameters = new Dictionary <string, object> { { nameof(HandlePropertiesChangedComponent.IntProperty), 123 } } }; var rootComponentId = renderer.AssignComponentId(component); // Act/Assert 0: Initial render renderer.RenderNewBatch(rootComponentId); var batch1 = renderer.Batches.Single(); var childComponentFrame = batch1 .ReferenceFrames.Where(frame => frame.FrameType == RenderTreeFrameType.Component).Single(); var childComponentId = childComponentFrame.ComponentId; var diffForChildComponent0 = batch1.DiffsByComponentId[childComponentId].Single(); var childComponentInstance = (HandlePropertiesChangedComponent)childComponentFrame.Component; Assert.Equal(1, childComponentInstance.NotificationsCount); Assert.Collection(diffForChildComponent0.Edits, edit => { Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); AssertFrame.Text(batch1.ReferenceFrames[edit.ReferenceFrameIndex], "Notifications: 1", 0); }); // Act/Assert 1: If properties didn't change, we don't notify renderer.RenderNewBatch(rootComponentId); Assert.Equal(1, childComponentInstance.NotificationsCount); // Act/Assert 2: If properties did change, we do notify component.ChildParameters[nameof(HandlePropertiesChangedComponent.IntProperty)] = 456; renderer.RenderNewBatch(rootComponentId); Assert.Equal(2, childComponentInstance.NotificationsCount); var batch3 = renderer.Batches.Skip(2).Single(); var diffForChildComponent2 = batch3.DiffsByComponentId[childComponentId].Single(); Assert.Equal(2, childComponentInstance.NotificationsCount); AssertFrame.Text(batch3.ReferenceFrames[0], "Notifications: 2", 0); }
public void CannotRenderUnknownComponents() { // Arrange var renderer = new TestRenderer(); // Act/Assert Assert.Throws <ArgumentException>(() => { renderer.RenderNewBatch(123); }); }
public void PreservesChildComponentInstancesWithNoAttributes() { // Arrange: First render, capturing child component instance var renderer = new TestRenderer(); var message = "Hello"; var component = new TestComponent(builder => { builder.AddText(0, message); builder.OpenComponent <MessageComponent>(1); builder.CloseComponent(); }); var rootComponentId = renderer.AssignComponentId(component); renderer.RenderNewBatch(rootComponentId); var nestedComponentFrame = renderer.Batches.Single() .DiffsByComponentId[rootComponentId] .Single() .ReferenceFrames .Single(frame => frame.FrameType == RenderTreeFrameType.Component); var nestedComponentInstance = (MessageComponent)nestedComponentFrame.Component; // Act: Second render message = "Modified message"; renderer.RenderNewBatch(rootComponentId); // Assert var batch = renderer.Batches[1]; var diff = batch.DiffsByComponentId[rootComponentId].Single(); Assert.Collection(diff.Edits, edit => { Assert.Equal(RenderTreeEditType.UpdateText, edit.Type); Assert.Equal(0, edit.ReferenceFrameIndex); }); Assert.Collection(diff.ReferenceFrames, frame => AssertFrame.Text(frame, "Modified message")); Assert.False(batch.DiffsByComponentId.ContainsKey(nestedComponentFrame.ComponentId)); }
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 RenderBatchIncludesListOfDisposedComponents() { // Arrange var renderer = new TestRenderer(); var firstRender = true; var component = new TestComponent(builder => { builder.OpenElement(7, "some element"); if (firstRender) { builder.OpenComponent <FakeComponent>(100); builder.CloseComponent(); builder.OpenComponent <FakeComponent>(150); builder.CloseComponent(); } builder.OpenComponent <FakeComponent>(200); builder.CloseComponent(); builder.CloseElement(); }); var rootComponentId = renderer.AssignComponentId(component); // Act/Assert 1: First render, capturing child component IDs renderer.RenderNewBatch(rootComponentId); var childComponentIds = renderer.Batches.Single().DiffsByComponentId[rootComponentId] .Single() .ReferenceFrames .Where(frame => frame.FrameType == RenderTreeFrameType.Component) .Select(frame => frame.ComponentId) .ToList(); Assert.Equal(childComponentIds, new[] { 1, 2, 3 }); // Act: Second render firstRender = false; renderer.RenderNewBatch(rootComponentId); // Assert: Applicable children are included in disposal list Assert.Equal(renderer.Batches[1].DisposedComponentIDs, new[] { 1, 2 }); }
public void RenderBatchIncludesListOfDisposedComponents() { // Arrange var renderer = new TestRenderer(); var firstRender = true; var component = new TestComponent(builder => { if (firstRender) { // Nested descendants builder.OpenComponent <ConditionalParentComponent <FakeComponent> >(100); builder.AddAttribute(101, nameof(ConditionalParentComponent <FakeComponent> .IncludeChild), true); builder.CloseComponent(); } builder.OpenComponent <FakeComponent>(200); builder.CloseComponent(); }); var rootComponentId = renderer.AssignComponentId(component); // Act/Assert 1: First render, capturing child component IDs renderer.RenderNewBatch(rootComponentId); var batch = renderer.Batches.Single(); var rootComponentDiff = batch.DiffsByComponentId[rootComponentId].Single(); var childComponentIds = rootComponentDiff .Edits .Select(edit => batch.ReferenceFrames[edit.ReferenceFrameIndex]) .Where(frame => frame.FrameType == RenderTreeFrameType.Component) .Select(frame => frame.ComponentId) .ToList(); Assert.Equal(new[] { 1, 2 }, childComponentIds); // Act: Second render firstRender = false; renderer.RenderNewBatch(rootComponentId); // Assert: Applicable children are included in disposal list Assert.Equal(new[] { 1, 3 }, renderer.Batches[1].DisposedComponentIDs); }
public void UpdatesPropertiesOnRetainedChildComponentInstances() { // Arrange: First render, capturing child component instance var renderer = new TestRenderer(); var objectThatWillNotChange = new object(); var firstRender = true; var component = new TestComponent(builder => { builder.OpenComponent <FakeComponent>(1); builder.AddAttribute(2, nameof(FakeComponent.IntProperty), firstRender ? 123 : 256); builder.AddAttribute(3, nameof(FakeComponent.ObjectProperty), objectThatWillNotChange); builder.AddAttribute(4, nameof(FakeComponent.StringProperty), firstRender ? "String that will change" : "String that did change"); builder.CloseComponent(); }); var rootComponentId = renderer.AssignComponentId(component); renderer.RenderNewBatch(rootComponentId); var originalComponentFrame = renderer.Batches.Single().DiffsByComponentId[rootComponentId] .Single() .ReferenceFrames .Single(frame => frame.FrameType == RenderTreeFrameType.Component); var childComponentInstance = (FakeComponent)originalComponentFrame.Component; // Assert 1: properties were assigned Assert.Equal(123, childComponentInstance.IntProperty); Assert.Equal("String that will change", childComponentInstance.StringProperty); Assert.Same(objectThatWillNotChange, childComponentInstance.ObjectProperty); // Act: Second render firstRender = false; renderer.RenderNewBatch(rootComponentId); // Assert Assert.Equal(256, childComponentInstance.IntProperty); Assert.Equal("String that did change", childComponentInstance.StringProperty); Assert.Same(objectThatWillNotChange, childComponentInstance.ObjectProperty); }
public void NotifiesIHandlePropertiesChangedBeforeFirstRender() { // Arrange var renderer = new TestRenderer(); var component = new HandlePropertiesChangedComponent(); var rootComponentId = renderer.AssignComponentId(component); // Act renderer.RenderNewBatch(rootComponentId); // Assert AssertFrame.Text(renderer.Batches.Single().ReferenceFrames[0], "Notifications: 1", 0); }
public void CanReRenderTopLevelComponents() { // Arrange var renderer = new TestRenderer(); var component = new MessageComponent { Message = "Initial message" }; var componentId = renderer.AssignComponentId(component); // Act/Assert: first render renderer.RenderNewBatch(componentId); var firstDiff = renderer.Batches.Single().DiffsByComponentId[componentId].Single(); Assert.Collection(firstDiff.Edits, edit => { Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); Assert.Equal(0, edit.ReferenceFrameIndex); }); Assert.Collection(firstDiff.ReferenceFrames, frame => AssertFrame.Text(frame, "Initial message")); // Act/Assert: second render component.Message = "Modified message"; renderer.RenderNewBatch(componentId); var secondDiff = renderer.Batches.Skip(1).Single().DiffsByComponentId[componentId].Single(); Assert.Collection(firstDiff.Edits, edit => { Assert.Equal(RenderTreeEditType.UpdateText, edit.Type); Assert.Equal(0, edit.ReferenceFrameIndex); }); Assert.Collection(firstDiff.ReferenceFrames, frame => AssertFrame.Text(frame, "Modified message")); }
public void ComponentsCanBeAssociatedWithMultipleRenderers() { // Arrange var renderer1 = new TestRenderer(); var renderer2 = new TestRenderer(); var component = new MessageComponent { Message = "Hello, world!" }; var renderer1ComponentId = renderer1.AssignComponentId(component); renderer2.AssignComponentId(new TestComponent(null)); // Just so they don't get the same IDs var renderer2ComponentId = renderer2.AssignComponentId(component); // Act/Assert: Render component in renderer1 renderer1.RenderNewBatch(renderer1ComponentId); Assert.True(renderer1.Batches.Single().DiffsByComponentId.ContainsKey(renderer1ComponentId)); Assert.Empty(renderer2.Batches); // Act/Assert: Render same component in renderer2 renderer2.RenderNewBatch(renderer2ComponentId); Assert.True(renderer2.Batches.Single().DiffsByComponentId.ContainsKey(renderer2ComponentId)); }