public void RendersChildContentIfAuthorized()
    {
        // Arrange
        var authorizationService = new TestAuthorizationService();

        authorizationService.NextResult = AuthorizationResult.Success();
        var renderer      = CreateTestRenderer(authorizationService);
        var rootComponent = WrapInAuthorizeView(
            childContent: context => builder =>
            builder.AddContent(0, $"You are authenticated as {context.User.Identity.Name}"));

        rootComponent.AuthenticationState = CreateAuthenticationState("Nellie");

        // Act
        renderer.AssignRootComponentId(rootComponent);
        rootComponent.TriggerRender();

        // Assert
        var diff = renderer.Batches.Single().GetComponentDiffs <AuthorizeView>().Single();

        Assert.Collection(diff.Edits, edit =>
        {
            Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
            AssertFrame.Text(
                renderer.Batches.Single().ReferenceFrames[edit.ReferenceFrameIndex],
                "You are authenticated as Nellie");
        });

        // Assert: The IAuthorizationService was given expected criteria
        Assert.Collection(authorizationService.AuthorizeCalls, call =>
        {
            Assert.Equal("Nellie", call.user.Identity.Name);
            Assert.Null(call.resource);
            Assert.Collection(call.requirements,
                              req => Assert.IsType <DenyAnonymousAuthorizationRequirement>(req));
        });
    }
    public void RespondsToNotificationsFromAuthenticationStateProvider()
    {
        // Arrange: Service
        var services          = new ServiceCollection();
        var authStateProvider = new TestAuthenticationStateProvider()
        {
            CurrentAuthStateTask = Task.FromResult(CreateAuthenticationState(null))
        };

        services.AddSingleton <AuthenticationStateProvider>(authStateProvider);

        // Arrange: Renderer and component, initially rendered
        var renderer  = new TestRenderer(services.BuildServiceProvider());
        var component = new UseCascadingAuthenticationStateComponent();

        renderer.AssignRootComponentId(component);
        component.TriggerRender();
        var receiveAuthStateId = renderer.Batches.Single()
                                 .GetComponentFrames <ReceiveAuthStateComponent>().Single().ComponentId;

        // Act 2: AuthenticationStateProvider issues notification
        authStateProvider.TriggerAuthenticationStateChanged(
            Task.FromResult(CreateAuthenticationState("Bert")));

        // Assert 2: Re-renders content
        Assert.Equal(2, renderer.Batches.Count);
        var batch = renderer.Batches.Last();
        var receiveAuthStateDiff = batch.DiffsByComponentId[receiveAuthStateId].Single();

        Assert.Collection(receiveAuthStateDiff.Edits, edit =>
        {
            Assert.Equal(RenderTreeEditType.UpdateText, edit.Type);
            AssertFrame.Text(
                batch.ReferenceFrames[edit.ReferenceFrameIndex],
                "Authenticated: True; Name: Bert; Pending: False; Renders: 2");
        });
    }
示例#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, "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);
        }
        public void SkipsUnmodifiedTrailingSiblings()
        {
            // Arrange
            oldTree.AddContent(10, "text1");
            oldTree.AddContent(11, "text2");
            oldTree.AddContent(12, "text3");
            oldTree.AddContent(13, "text4");
            newTree.AddContent(10, "text1");
            newTree.AddContent(11, "text2modified");
            newTree.AddContent(12, "text3");
            newTree.AddContent(13, "text4");

            // Act
            var(result, referenceFrames) = GetSingleUpdatedComponent();

            // Assert
            Assert.Collection(result.Edits,
                              entry =>
            {
                AssertEdit(entry, RenderTreeEditType.UpdateText, 1);
                Assert.Equal(0, entry.ReferenceFrameIndex);
            });
            AssertFrame.Text(referenceFrames[0], "text2modified", 11);
        }
        public void RazorTemplate_Generic_CanBeUsedFromMethod()
        {
            // Arrange
            var component = CompileToComponent(@"
@(Repeat((context) => @<div>@context.ToLower()</div>, ""Hello, World!"", 3))

@functions {
    RenderFragment Repeat<T>(RenderFragment<T> template, T value, int count)
    {
        return (b) =>
        {
            for (var i = 0; i < count; i++)
            {
                b.AddContent(i, template, value);
            }
        };
    }
}");

            // Act
            var frames = GetRenderTree(component);

            // Assert
            //
            // The sequence numbers start at 1 here because there is an AddContent(0, Repeat(....) call
            // that precedes the definition of the lambda. Sequence numbers for the lambda are allocated
            // from the same logical sequence as the surrounding code.
            Assert.Collection(
                frames,
                frame => AssertFrame.Element(frame, "div", 2, 1),
                frame => AssertFrame.Text(frame, "hello, world!", 2),
                frame => AssertFrame.Element(frame, "div", 2, 1),
                frame => AssertFrame.Text(frame, "hello, world!", 2),
                frame => AssertFrame.Element(frame, "div", 2, 1),
                frame => AssertFrame.Text(frame, "hello, world!", 2));
        }
示例#6
0
        public void CanIncludeChildrenInComponents()
        {
            // Arrange/Act
            var testComponentTypeName = FullTypeName<TestComponent>();
            var component = CompileToComponent($"<c:{testComponentTypeName} MyAttr=@(\"abc\")>" +
                $"Some text" +
                $"<some-child a='1'>Nested text</some-child>" +
                $"</c:{testComponentTypeName}>");
            var frames = GetRenderTree(component);

            // Assert: component frames are correct
            Assert.Collection(frames,
                frame => AssertFrame.Component<TestComponent>(frame, 3, 0),
                frame => AssertFrame.Attribute(frame, "MyAttr", "abc", 1),
                frame => AssertFrame.Attribute(frame, RenderTreeBuilder.ChildContent, 2));

            // Assert: Captured ChildContent frames are correct
            var childFrames = GetFrames((RenderFragment)frames[2].AttributeValue);
            Assert.Collection(childFrames,
                frame => AssertFrame.Text(frame, "Some text", 3),
                frame => AssertFrame.Element(frame, "some-child", 3, 4),
                frame => AssertFrame.Attribute(frame, "a", "1", 5),
                frame => AssertFrame.Text(frame, "Nested text", 6));
        }
示例#7
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 RazorTemplate_Generic_CanBeUsedFromRazorCode()
        {
            // Arrange
            var component = CompileToComponent(@"
@{ RenderFragment<string> template = (context) => @<div>@context.ToLower()</div>; }
@for (var i = 0; i < 3; i++)
{
    @template(""Hello, World!"");
}
");

            // Act
            var frames = GetRenderTree(component);

            // Assert
            Assert.Collection(
                frames,
                frame => AssertFrame.Element(frame, "div", 2, 0),
                frame => AssertFrame.Text(frame, "hello, world!", 1),
                frame => AssertFrame.Element(frame, "div", 2, 0),
                frame => AssertFrame.Text(frame, "hello, world!", 1),
                frame => AssertFrame.Element(frame, "div", 2, 0),
                frame => AssertFrame.Text(frame, "hello, world!", 1));
        }
        public void Render_GenericComponent_WithoutChildContent()
        {
            // Arrange
            AdditionalSyntaxTrees.Add(GenericContextComponent);

            var component = CompileToComponent(@"
<GenericContext TItem=int Items=""@(new List<int>() { 1, 2, })"" />");

            // Act
            var frames = GetRenderTree(component);

            // Assert
            var genericComponentType = component.GetType().Assembly.DefinedTypes
                                       .Where(t => t.Name == "GenericContext`1")
                                       .Single()
                                       .MakeGenericType(typeof(int));

            Assert.Collection(
                frames,
                frame => AssertFrame.Component(frame, genericComponentType.FullName, 2, 0),
                frame => AssertFrame.Attribute(frame, "Items", typeof(List <int>), 1),
                frame => AssertFrame.Text(frame, "1", 0),
                frame => AssertFrame.Text(frame, "2", 1));
        }
        public void SupportsCSharpExpressions()
        {
            // Arrange/Act
            var component = CompileToComponent(@"
                @(""Hello"")
                @((object)null)
                @(123)
                @(new object())
            ");

            // Assert
            var frames = GetRenderTree(component);

            Assert.Collection(frames,
                              frame => AssertFrame.Whitespace(frame, 0),
                              frame => AssertFrame.Text(frame, "Hello", 1),
                              frame => AssertFrame.Whitespace(frame, 2),
                              frame => AssertFrame.Whitespace(frame, 3), // @((object)null)
                              frame => AssertFrame.Whitespace(frame, 4),
                              frame => AssertFrame.Text(frame, "123", 5),
                              frame => AssertFrame.Whitespace(frame, 6),
                              frame => AssertFrame.Text(frame, new object().ToString(), 7),
                              frame => AssertFrame.Whitespace(frame, 8));
        }
        public void SkipsUnmodifiedSubtrees()
        {
            // Arrange
            oldTree.OpenElement(10, "root");
            oldTree.AddText(11, "Text that will change");
            oldTree.OpenElement(12, "Subtree that will not change");
            oldTree.OpenElement(13, "Another");
            oldTree.AddText(14, "Text that will not change");
            oldTree.CloseElement();
            oldTree.CloseElement();
            oldTree.CloseElement();

            newTree.OpenElement(10, "root");
            newTree.AddText(11, "Text that has changed");
            newTree.OpenElement(12, "Subtree that will not change");
            newTree.OpenElement(13, "Another");
            newTree.AddText(14, "Text that will not change");
            newTree.CloseElement();
            newTree.CloseElement();
            newTree.CloseElement();

            // Act
            var result = GetSingleUpdatedComponent();

            // Assert
            Assert.Collection(result.Edits,
                              entry => AssertEdit(entry, RenderTreeEditType.StepIn, 0),
                              entry =>
            {
                AssertEdit(entry, RenderTreeEditType.UpdateText, 0);
                Assert.Equal(0, entry.ReferenceFrameIndex);
            },
                              entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0));
            Assert.Collection(result.ReferenceFrames,
                              frame => AssertFrame.Text(frame, "Text that has changed", 11));
        }
示例#12
0
        public void BuildRenderTree_ForPropertyWithDisplayAttributeAndStringLocalizer_Should_CreateCorrectFramesUsingStringLocalizer()
        {
            // Arrange
            var stringLocalizerMock = new Mock <IStringLocalizer>();

            stringLocalizerMock.Setup(l => l[It.IsAny <string>()]).Returns(new LocalizedString("name", "value"));

            _sut.StringLocalizerMock = stringLocalizerMock.Object;
            _sut.For = () => _model.First;

            // Act
            var renderTreeBuilder = new RenderTreeBuilder();

            _sut.BuildRenderTreeMock(renderTreeBuilder);

            // Assert
            var frames = renderTreeBuilder.GetFrames().Array;

            AssertFrame.Element(frames[0], "label", 2);
            AssertFrame.Text(frames[1], "value");

            // Verify
            stringLocalizerMock.Verify(localizer => localizer["Person_First"], Times.Once);
        }
示例#13
0
        public void Render_BodyChildContent_Generic()
        {
            // Arrange
            AdditionalSyntaxTrees.Add(RenderChildContentStringComponent);

            var component = CompileToComponent(@"
<RenderChildContentString Value=""HI"">
  <div>@context.ToLowerInvariant()</div>
</RenderChildContentString>");

            // Act
            var frames = GetRenderTree(component);

            // Assert
            Assert.Collection(
                frames,
                frame => AssertFrame.Component(frame, "Test.RenderChildContentString", 3, 0),
                frame => AssertFrame.Attribute(frame, "Value", "HI", 1),
                frame => AssertFrame.Attribute(frame, "ChildContent", 2),
                frame => AssertFrame.MarkupWhitespace(frame, 3),
                frame => AssertFrame.Element(frame, "div", 2, 4),
                frame => AssertFrame.Text(frame, "hi", 5),
                frame => AssertFrame.MarkupWhitespace(frame, 6));
        }
示例#14
0
    public void CanRenderComponentByTypeWithParameters()
    {
        // Arrange
        var instance        = new DynamicComponent();
        var renderer        = new TestRenderer();
        var childParameters = new Dictionary <string, object>
        {
            { nameof(TestComponent.IntProp), 123 },
            { nameof(TestComponent.ChildContent), (RenderFragment)(builder =>
                {
                    builder.AddContent(0, "This is some child content");
                }) },
        };
        var parameters = ParameterView.FromDictionary(new Dictionary <string, object>
        {
            { nameof(DynamicComponent.Type), typeof(TestComponent) },
            { nameof(DynamicComponent.Parameters), childParameters },
        });

        // Act
        renderer.RenderRootComponent(
            renderer.AssignRootComponentId(instance),
            parameters);

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

        // It renders a reference to the child component with its parameters
        AssertFrame.Component <TestComponent>(batch.ReferenceFrames[0], 4, 0);
        AssertFrame.Attribute(batch.ReferenceFrames[1], nameof(TestComponent.IntProp), 123, 1);
        AssertFrame.Attribute(batch.ReferenceFrames[2], nameof(TestComponent.ChildContent), 1);

        // The child component itself is rendered
        AssertFrame.Text(batch.ReferenceFrames[4], "Hello from TestComponent with IntProp=123", 0);
        AssertFrame.Text(batch.ReferenceFrames[5], "This is some child content", 0);
    }
示例#15
0
    public void RendersAuthorizingUntilAuthorizationCompleted()
    {
        // Arrange
        var @event = new ManualResetEventSlim();
        var authorizationService = new TestAuthorizationService();

        authorizationService.NextResult = AuthorizationResult.Success();
        var renderer = CreateTestRenderer(authorizationService);

        renderer.OnUpdateDisplayComplete = () => { @event.Set(); };
        var rootComponent = WrapInAuthorizeView(
            authorizing: builder => builder.AddContent(0, "Auth pending..."),
            authorized: context => builder => builder.AddContent(0, $"Hello, {context.User.Identity.Name}!"));
        var authTcs = new TaskCompletionSource <AuthenticationState>();

        rootComponent.AuthenticationState = authTcs.Task;

        // Act/Assert 1: Auth pending
        renderer.AssignRootComponentId(rootComponent);
        rootComponent.TriggerRender();
        var batch1 = renderer.Batches.Single();
        var authorizeViewComponentId = batch1.GetComponentFrames <AuthorizeView>().Single().ComponentId;
        var diff1 = batch1.DiffsByComponentId[authorizeViewComponentId].Single();

        Assert.Collection(diff1.Edits, edit =>
        {
            Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
            AssertFrame.Text(
                batch1.ReferenceFrames[edit.ReferenceFrameIndex],
                "Auth pending...");
        });

        // Act/Assert 2: Auth process completes asynchronously
        @event.Reset();
        authTcs.SetResult(CreateAuthenticationState("Monsieur").Result);

        // We need to wait here because the continuations of SetResult will be scheduled to run asynchronously.
        @event.Wait(Timeout);

        Assert.Equal(2, renderer.Batches.Count);
        var batch2 = renderer.Batches[1];
        var diff2  = batch2.DiffsByComponentId[authorizeViewComponentId].Single();

        Assert.Collection(diff2.Edits, edit =>
        {
            Assert.Equal(RenderTreeEditType.UpdateText, edit.Type);
            Assert.Equal(0, edit.SiblingIndex);
            AssertFrame.Text(
                batch2.ReferenceFrames[edit.ReferenceFrameIndex],
                "Hello, Monsieur!");
        });

        // Assert: The IAuthorizationService was given expected criteria
        Assert.Collection(authorizationService.AuthorizeCalls, call =>
        {
            Assert.Equal("Monsieur", call.user.Identity.Name);
            Assert.Null(call.resource);
            Assert.Collection(call.requirements,
                              req => Assert.IsType <DenyAnonymousAuthorizationRequirement>(req));
        });
    }
示例#16
0
    public async Task RendersAuthorizingUntilAuthorizationCompletedAsync()
    {
        // Covers https://github.com/dotnet/aspnetcore/pull/31794
        // Arrange
        var authorizationService = new TestAsyncAuthorizationService();

        authorizationService.NextResult = AuthorizationResult.Success();
        var renderer = CreateTestRenderer(authorizationService);

        renderer.OnUpdateDisplayComplete = () => { };
        var rootComponent = WrapInAuthorizeView(
            authorizing: builder => builder.AddContent(0, "Auth pending..."),
            authorized: context => builder => builder.AddContent(0, $"Hello, {context.User.Identity.Name}!"));

        var authTcs = new TaskCompletionSource <AuthenticationState>();

        rootComponent.AuthenticationState = authTcs.Task;

        // Act/Assert 1: Auth pending
        renderer.AssignRootComponentId(rootComponent);
        rootComponent.TriggerRender();
        var batch1 = Assert.Single(renderer.Batches);
        var authorizeViewComponentId = Assert.Single(batch1.GetComponentFrames <AuthorizeView>()).ComponentId;
        var diff1 = Assert.Single(batch1.DiffsByComponentId[authorizeViewComponentId]);

        Assert.Collection(diff1.Edits, edit =>
        {
            Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
            AssertFrame.Text(
                batch1.ReferenceFrames[edit.ReferenceFrameIndex],
                "Auth pending...");
        });

        // We need to do this because the continuation from the TCS might run asynchronously
        // (This wouldn't happen under the sync context or in wasm)
        var renderTcs = new TaskCompletionSource();

        renderer.OnUpdateDisplayComplete = () => renderTcs.SetResult();
        authTcs.SetResult(CreateAuthenticationState("Monsieur").Result);

        await renderTcs.Task;

        Assert.Equal(2, renderer.Batches.Count);
        var batch2 = renderer.Batches[1];
        var diff2  = Assert.Single(batch2.DiffsByComponentId[authorizeViewComponentId]);

        Assert.Collection(diff2.Edits, edit =>
        {
            Assert.Equal(RenderTreeEditType.UpdateText, edit.Type);
            Assert.Equal(0, edit.SiblingIndex);
            AssertFrame.Text(
                batch2.ReferenceFrames[edit.ReferenceFrameIndex],
                "Hello, Monsieur!");
        });

        // Assert: The IAuthorizationService was given expected criteria
        Assert.Collection(authorizationService.AuthorizeCalls, call =>
        {
            Assert.Equal("Monsieur", call.user.Identity.Name);
            Assert.Null(call.resource);
            Assert.Collection(call.requirements,
                              req => Assert.IsType <DenyAnonymousAuthorizationRequirement>(req));
        });
    }
示例#17
0
    public void CanChangeLayout()
    {
        // Arrange
        var setParametersTask1 = _renderer.Dispatcher.InvokeAsync(() => _layoutViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary <string, object>
        {
            { nameof(LayoutView.Layout), typeof(NestedLayout) },
            { nameof(LayoutView.ChildContent), (RenderFragment)(builder => {
                    builder.AddContent(0, "Some content");
                }) }
        })));

        Assert.True(setParametersTask1.IsCompletedSuccessfully);

        // Act
        var setParametersTask2 = _renderer.Dispatcher.InvokeAsync(() => _layoutViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary <string, object>
        {
            { nameof(LayoutView.Layout), typeof(OtherNestedLayout) },
        })));

        // Assert
        Assert.True(setParametersTask2.IsCompletedSuccessfully);
        Assert.Equal(2, _renderer.Batches.Count);
        var batch = _renderer.Batches[1];

        Assert.Equal(1, batch.DisposedComponentIDs.Count);  // Disposes NestedLayout
        Assert.Collection(batch.DiffsInOrder,
                          diff => Assert.Empty(diff.Edits), // LayoutView rerendered, but with no changes
                          diff =>
        {
            // RootLayout rerendered, changing child
            Assert.Collection(diff.Edits,
                              edit =>
            {
                Assert.Equal(RenderTreeEditType.RemoveFrame, edit.Type);
                Assert.Equal(1, edit.SiblingIndex);
            },
                              edit =>
            {
                Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
                Assert.Equal(1, edit.SiblingIndex);
                AssertFrame.Component <OtherNestedLayout>(
                    batch.ReferenceFrames[edit.ReferenceFrameIndex],
                    sequence: 0);
            });
        },
                          diff =>
        {
            // Inserts new OtherNestedLayout
            Assert.Collection(diff.Edits,
                              edit =>
            {
                Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
                Assert.Equal(0, edit.SiblingIndex);
                AssertFrame.Text(
                    batch.ReferenceFrames[edit.ReferenceFrameIndex],
                    "OtherNestedLayout starts here");
            },
                              edit =>
            {
                Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
                Assert.Equal(1, edit.SiblingIndex);
                AssertFrame.Text(
                    batch.ReferenceFrames[edit.ReferenceFrameIndex],
                    "Some content");
            },
                              edit =>
            {
                Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
                Assert.Equal(2, edit.SiblingIndex);
                AssertFrame.Text(
                    batch.ReferenceFrames[edit.ReferenceFrameIndex],
                    "OtherNestedLayout ends here");
            });
        });
    }
示例#18
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);
        }
示例#19
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");
                });
            });
        }
示例#20
0
        public void Render_HtmlBlock_Integration()
        {
            // Arrange
            AdditionalSyntaxTrees.Add(Parse(@"
using Microsoft.AspNetCore.Components;
namespace Test
{
    public class MyComponent : ComponentBase
    {
        [Parameter]
        public RenderFragment ChildContent { get; set; }
    }
}
"));

            var component = CompileToComponent(@"
<html>
  <head><meta><meta></head>
  <body>
    <MyComponent>
      <div><span></span><span></span></div>
      <div>@(""hi"")</div>
      <div><span></span><span></span></div>
      <div></div>
      <div>@(""hi"")</div>
      <div></div>
  </MyComponent>
  </body>
</html>");

            // Act
            var frames = GetRenderTree(component);

            // Assert: component frames are correct
            Assert.Collection(
                frames,
                frame => AssertFrame.Element(frame, "html", 9, 0),
                frame => AssertFrame.MarkupWhitespace(frame, 1),
                frame => AssertFrame.Markup(frame, "<head><meta><meta></head>\n  ", 2),
                frame => AssertFrame.Element(frame, "body", 5, 3),
                frame => AssertFrame.MarkupWhitespace(frame, 4),
                frame => AssertFrame.Component(frame, "Test.MyComponent", 2, 5),
                frame => AssertFrame.Attribute(frame, "ChildContent", 6),
                frame => AssertFrame.MarkupWhitespace(frame, 16),
                frame => AssertFrame.MarkupWhitespace(frame, 17));

            // Assert: Captured ChildContent frames are correct
            var childFrames = GetFrames((RenderFragment)frames[6].AttributeValue);

            Assert.Collection(
                childFrames.AsEnumerable(),
                frame => AssertFrame.MarkupWhitespace(frame, 7),
                frame => AssertFrame.Markup(frame, "<div><span></span><span></span></div>\n      ", 8),
                frame => AssertFrame.Element(frame, "div", 2, 9),
                frame => AssertFrame.Text(frame, "hi", 10),
                frame => AssertFrame.MarkupWhitespace(frame, 11),
                frame => AssertFrame.Markup(frame, "<div><span></span><span></span></div>\n      <div></div>\n      ", 12),
                frame => AssertFrame.Element(frame, "div", 2, 13),
                frame => AssertFrame.Text(frame, "hi", 14),
                frame => AssertFrame.Markup(frame, "\n      <div></div>\n  ", 15));
        }
示例#21
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, "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
        }