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"); }); }
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)); }
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)); }
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)); }
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); }
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)); }
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); }
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)); }); }
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)); }); }
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"); }); }); }
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); }
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"); }); }); }
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)); }
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 }