Esempio n. 1
0
        public void DoesNotNotifyDescendantsIfCascadingParameterValuesAreImmutableAndUnchanged()
        {
            // Arrange
            var renderer  = new TestRenderer();
            var component = new TestComponent(builder =>
            {
                builder.OpenComponent <CascadingValue <string> >(0);
                builder.AddAttribute(1, "Value", "Unchanging value");
                builder.AddAttribute(2, RenderTreeBuilder.ChildContent, new RenderFragment(childBuilder =>
                {
                    childBuilder.OpenComponent <CascadingParameterConsumerComponent <string> >(0);
                    childBuilder.AddAttribute(1, "RegularParameter", "Goodbye");
                    childBuilder.CloseComponent();
                }));
                builder.CloseComponent();
            });

            // Act 1: Initial render
            var componentId = renderer.AssignRootComponentId(component);

            component.TriggerRender();
            var firstBatch      = renderer.Batches.Single();
            var nestedComponent = FindComponent <CascadingParameterConsumerComponent <string> >(firstBatch, out _);

            Assert.Equal(3, firstBatch.DiffsByComponentId.Count); // Root + CascadingValue + nested
            Assert.Equal(1, nestedComponent.NumRenders);

            // Act/Assert: Re-render the CascadingValue; observe nested component wasn't re-rendered
            component.TriggerRender();

            // Assert: We did not re-render CascadingParameterConsumerComponent
            Assert.Equal(2, renderer.Batches.Count);
            var secondBatch = renderer.Batches[1];

            Assert.Equal(2, secondBatch.DiffsByComponentId.Count); // Root + CascadingValue, but not nested one
            Assert.Equal(1, nestedComponent.NumRenders);
        }
Esempio n. 2
0
        public void CanRenderComponentsIfNotGCed()
        {
            // Arrange
            var renderer  = new NoOpRenderer();
            var didRender = false;

            // Act
            var component = new TestComponent(builder =>
            {
                didRender = true;
            });
            var componentId = renderer.AssignComponentId(component);

            // Unlike the preceding test, we still have a reference to the component
            // instance on the stack here, so the following should not cause it to
            // be collected. Then when we call RenderComponent, there should be no error.
            GC.Collect();
            GC.WaitForPendingFinalizers();

            renderer.RenderNewBatch(componentId);

            // Assert
            Assert.True(didRender);
        }
Esempio n. 3
0
        public void PassesCascadingParametersToNestedComponents()
        {
            // Arrange
            var renderer  = new TestRenderer();
            var component = new TestComponent(builder =>
            {
                builder.OpenComponent <CascadingValue <string> >(0);
                builder.AddAttribute(1, "Value", "Hello");
                builder.AddAttribute(2, RenderTreeBuilder.ChildContent, new RenderFragment(childBuilder =>
                {
                    childBuilder.OpenComponent <CascadingParameterConsumerComponent <string> >(0);
                    childBuilder.AddAttribute(1, "RegularParameter", "Goodbye");
                    childBuilder.CloseComponent();
                }));
                builder.CloseComponent();
            });

            // Act/Assert
            var componentId = renderer.AssignRootComponentId(component);

            component.TriggerRender();
            var batch               = renderer.Batches.Single();
            var nestedComponent     = FindComponent <CascadingParameterConsumerComponent <string> >(batch, out var nestedComponentId);
            var nestedComponentDiff = batch.DiffsByComponentId[nestedComponentId].Single();

            // The nested component was rendered with the correct parameters
            Assert.Collection(nestedComponentDiff.Edits,
                              edit =>
            {
                Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
                AssertFrame.Text(
                    batch.ReferenceFrames[edit.ReferenceFrameIndex],
                    "CascadingParameter=Hello; RegularParameter=Goodbye");
            });
            Assert.Equal(1, nestedComponent.NumRenders);
        }
Esempio n. 4
0
        public void ComponentCanTriggerRenderWhenExistingBatchIsInProgress()
        {
            // Arrange
            var           renderer          = new TestRenderer();
            TestComponent parent            = null;
            var           parentRenderCount = 0;

            parent = new TestComponent(builder =>
            {
                builder.OpenComponent <ReRendersParentComponent>(0);
                builder.AddAttribute(1, nameof(ReRendersParentComponent.Parent), parent);
                builder.CloseComponent();
                builder.AddContent(2, $"Parent render count: {++parentRenderCount}");
            });
            var parentComponentId = renderer.AssignComponentId(parent);

            // Act
            parent.TriggerRender();

            // Assert
            var batch = renderer.Batches.Single();

            Assert.Equal(4, batch.DiffsInOrder.Count);

            // First is the parent component's initial render
            var diff1 = batch.DiffsInOrder[0];

            Assert.Equal(parentComponentId, diff1.ComponentId);
            Assert.Collection(diff1.Edits,
                              edit =>
            {
                Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
                AssertFrame.Component <ReRendersParentComponent>(
                    batch.ReferenceFrames[edit.ReferenceFrameIndex]);
            },
                              edit =>
            {
                Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
                AssertFrame.Text(
                    batch.ReferenceFrames[edit.ReferenceFrameIndex],
                    "Parent render count: 1");
            });

            // Second is the child component's single render
            var diff2 = batch.DiffsInOrder[1];

            Assert.NotEqual(parentComponentId, diff2.ComponentId);
            var diff2edit = diff2.Edits.Single();

            Assert.Equal(RenderTreeEditType.PrependFrame, diff2edit.Type);
            AssertFrame.Text(batch.ReferenceFrames[diff2edit.ReferenceFrameIndex],
                             "Child is here");

            // Third is the parent's triggered render
            var diff3 = batch.DiffsInOrder[2];

            Assert.Equal(parentComponentId, diff3.ComponentId);
            var diff3edit = diff3.Edits.Single();

            Assert.Equal(RenderTreeEditType.UpdateText, diff3edit.Type);
            AssertFrame.Text(batch.ReferenceFrames[diff3edit.ReferenceFrameIndex],
                             "Parent render count: 2");

            // Fourth is child's rerender due to parent rendering
            var diff4 = batch.DiffsInOrder[3];

            Assert.NotEqual(parentComponentId, diff4.ComponentId);
            Assert.Empty(diff4.Edits);
        }
Esempio n. 5
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");
                });
            });
        }
Esempio n. 6
0
        public void DoesNotNotifyDescendantsOfUpdatedCascadingParameterValuesWhenFixed()
        {
            // Arrange
            var providedValue      = "Initial value";
            var shouldIncludeChild = true;
            var renderer           = new TestRenderer();
            var component          = new TestComponent(builder =>
            {
                builder.OpenComponent <CascadingValue <string> >(0);
                builder.AddAttribute(1, "Value", providedValue);
                builder.AddAttribute(2, "IsFixed", true);
                builder.AddAttribute(3, RenderTreeBuilder.ChildContent, new RenderFragment(childBuilder =>
                {
                    if (shouldIncludeChild)
                    {
                        childBuilder.OpenComponent <CascadingParameterConsumerComponent <string> >(0);
                        childBuilder.AddAttribute(1, "RegularParameter", "Goodbye");
                        childBuilder.CloseComponent();
                    }
                }));
                builder.CloseComponent();
            });

            // Act 1: Initial render; capture nested component ID
            var componentId = renderer.AssignRootComponentId(component);

            component.TriggerRender();
            var firstBatch      = renderer.Batches.Single();
            var nestedComponent = FindComponent <CascadingParameterConsumerComponent <string> >(firstBatch, out var nestedComponentId);

            Assert.Equal(1, nestedComponent.NumRenders);

            // Assert: Initial value is supplied to descendant
            var nestedComponentDiff = firstBatch.DiffsByComponentId[nestedComponentId].Single();

            Assert.Collection(nestedComponentDiff.Edits, edit =>
            {
                Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
                AssertFrame.Text(
                    firstBatch.ReferenceFrames[edit.ReferenceFrameIndex],
                    "CascadingParameter=Initial value; RegularParameter=Goodbye");
            });

            // Act 2: Re-render CascadingValue with new value
            providedValue = "Updated value";
            component.TriggerRender();

            // Assert: We did not re-render the descendant
            Assert.Equal(2, renderer.Batches.Count);
            var secondBatch = renderer.Batches[1];

            Assert.Equal(2, secondBatch.DiffsByComponentId.Count); // Root + CascadingValue, but not nested one
            Assert.Equal(1, nestedComponent.NumSetParametersCalls);
            Assert.Equal(1, nestedComponent.NumRenders);

            // Act 3: Dispose
            shouldIncludeChild = false;
            component.TriggerRender();

            // Assert: Absence of an exception here implies we didn't cause a problem by
            // trying to remove a non-existent subscription
        }