From 6e7c4ec6c0729011aec6e47d80a260807a046614 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 16 Feb 2018 17:22:36 +0000 Subject: [PATCH] Tests for LayoutDisplay component --- .../Helpers/IComponentExtensions.cs | 39 +++ .../LayoutTest.cs | 266 ++++++++++++++++++ test/shared/AssertFrame.cs | 1 - 3 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 test/Microsoft.AspNetCore.Blazor.Test/Helpers/IComponentExtensions.cs create mode 100644 test/Microsoft.AspNetCore.Blazor.Test/LayoutTest.cs diff --git a/test/Microsoft.AspNetCore.Blazor.Test/Helpers/IComponentExtensions.cs b/test/Microsoft.AspNetCore.Blazor.Test/Helpers/IComponentExtensions.cs new file mode 100644 index 0000000000..513494380a --- /dev/null +++ b/test/Microsoft.AspNetCore.Blazor.Test/Helpers/IComponentExtensions.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Blazor.Components; +using Microsoft.AspNetCore.Blazor.RenderTree; +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.Blazor.Test.Helpers +{ + public static class IComponentExtensions + { + public static void SetParameters( + this IComponent component, + Dictionary parameters) + { + component.SetParameters(DictionaryToParameterCollection(parameters)); + } + + private static ParameterCollection DictionaryToParameterCollection( + IDictionary parameters) + { + var builder = new RenderTreeBuilder(new TestRenderer()); + builder.OpenComponent(0); + foreach (var pair in parameters) + { + builder.AddAttribute(0, pair.Key, pair.Value); + } + builder.CloseElement(); + + return new ParameterCollection(builder.GetFrames().Array, 0); + } + + private abstract class AbstractComponent : IComponent + { + public abstract void Init(RenderHandle renderHandle); + public abstract void SetParameters(ParameterCollection parameters); + } + } +} diff --git a/test/Microsoft.AspNetCore.Blazor.Test/LayoutTest.cs b/test/Microsoft.AspNetCore.Blazor.Test/LayoutTest.cs new file mode 100644 index 0000000000..3a16601478 --- /dev/null +++ b/test/Microsoft.AspNetCore.Blazor.Test/LayoutTest.cs @@ -0,0 +1,266 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Blazor.Layouts; +using Microsoft.AspNetCore.Blazor.RenderTree; +using Microsoft.AspNetCore.Blazor.Test.Helpers; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Microsoft.AspNetCore.Blazor.Test +{ + public class LayoutTest + { + private TestRenderer _renderer = new TestRenderer(); + private LayoutDisplay _layoutDisplayComponent = new LayoutDisplay(); + private int _layoutDisplayComponentId; + + public LayoutTest() + { + _renderer = new TestRenderer(); + _layoutDisplayComponent = new LayoutDisplay(); + _layoutDisplayComponentId = _renderer.AssignComponentId(_layoutDisplayComponent); + } + + [Fact] + public void DisplaysComponentInsideLayout() + { + // Arrange/Act + _layoutDisplayComponent.SetParameters(new Dictionary + { + { nameof(LayoutDisplay.Page), typeof(ComponentWithLayout) } + }); + + // Assert + var batch = _renderer.Batches.Single(); + Assert.Collection(batch.DiffsInOrder, + diff => + { + // First is the LayoutDisplay component, which contains a RootLayout + var singleEdit = diff.Edits.Single(); + Assert.Equal(RenderTreeEditType.PrependFrame, singleEdit.Type); + AssertFrame.Component( + batch.ReferenceFrames[singleEdit.ReferenceFrameIndex]); + }, + diff => + { + // ... then a RootLayout which contains a ComponentWithLayout + // First is the LayoutDisplay component, which contains a RootLayout + Assert.Collection(diff.Edits, + edit => + { + Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); + AssertFrame.Text( + batch.ReferenceFrames[edit.ReferenceFrameIndex], + "RootLayout starts here"); + }, + edit => + { + Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); + AssertFrame.Component( + batch.ReferenceFrames[edit.ReferenceFrameIndex]); + }, + edit => + { + Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); + AssertFrame.Text( + batch.ReferenceFrames[edit.ReferenceFrameIndex], + "RootLayout ends here"); + }); + }, + diff => + { + // ... then the ComponentWithLayout + var singleEdit = diff.Edits.Single(); + Assert.Equal(RenderTreeEditType.PrependFrame, singleEdit.Type); + AssertFrame.Text( + batch.ReferenceFrames[singleEdit.ReferenceFrameIndex], + $"{nameof(ComponentWithLayout)} is here."); + }); + } + + [Fact] + public void DisplaysComponentInsideNestedLayout() + { + // Arrange/Act + _layoutDisplayComponent.SetParameters(new Dictionary + { + { nameof(LayoutDisplay.Page), typeof(ComponentWithNestedLayout) } + }); + + // Assert + var batch = _renderer.Batches.Single(); + Assert.Collection(batch.DiffsInOrder, + // First, a LayoutDisplay containing a RootLayout + diff => AssertFrame.Component( + batch.ReferenceFrames[diff.Edits[0].ReferenceFrameIndex]), + // Then a RootLayout containing a NestedLayout + diff => AssertFrame.Component( + batch.ReferenceFrames[diff.Edits[1].ReferenceFrameIndex]), + // Then a NestedLayout containing a ComponentWithNestedLayout + diff => AssertFrame.Component( + batch.ReferenceFrames[diff.Edits[1].ReferenceFrameIndex]), + // Then the ComponentWithNestedLayout + diff => AssertFrame.Text( + batch.ReferenceFrames[diff.Edits[0].ReferenceFrameIndex], + $"{nameof(ComponentWithNestedLayout)} is here.")); + } + + [Fact] + public void CanChangeDisplayedPageWithSameLayout() + { + // Arrange + _layoutDisplayComponent.SetParameters(new Dictionary + { + { nameof(LayoutDisplay.Page), typeof(ComponentWithLayout) } + }); + + // Act + _layoutDisplayComponent.SetParameters(new Dictionary + { + { nameof(LayoutDisplay.Page), typeof(DifferentComponentWithLayout) } + }); + + // Assert + Assert.Equal(2, _renderer.Batches.Count); + var batch = _renderer.Batches[1]; + Assert.Equal(1, batch.DisposedComponentIDs.Count); // Disposed only the inner page component + Assert.Collection(batch.DiffsInOrder, + diff => Assert.Empty(diff.Edits), // LayoutDisplay rerendered, but with no changes + diff => + { + // RootLayout rerendered + Assert.Collection(diff.Edits, + edit => + { + // Removed old page + Assert.Equal(RenderTreeEditType.RemoveFrame, edit.Type); + Assert.Equal(1, edit.SiblingIndex); + }, + edit => + { + // Inserted new one + Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); + Assert.Equal(1, edit.SiblingIndex); + AssertFrame.Component( + batch.ReferenceFrames[edit.ReferenceFrameIndex]); + }); + }, + diff => + { + // New page rendered + var singleEdit = diff.Edits.Single(); + Assert.Equal(RenderTreeEditType.PrependFrame, singleEdit.Type); + AssertFrame.Text( + batch.ReferenceFrames[singleEdit.ReferenceFrameIndex], + $"{nameof(DifferentComponentWithLayout)} is here."); + }); + } + + [Fact] + public void CanChangeDisplayedPageWithDifferentLayout() + { + // Arrange + _layoutDisplayComponent.SetParameters(new Dictionary + { + { nameof(LayoutDisplay.Page), typeof(ComponentWithLayout) } + }); + + // Act + _layoutDisplayComponent.SetParameters(new Dictionary + { + { nameof(LayoutDisplay.Page), typeof(ComponentWithNestedLayout) } + }); + + // Assert + Assert.Equal(2, _renderer.Batches.Count); + var batch = _renderer.Batches[1]; + Assert.Equal(1, batch.DisposedComponentIDs.Count); // Disposed only the inner page component + Assert.Collection(batch.DiffsInOrder, + diff => Assert.Empty(diff.Edits), // LayoutDisplay rerendered, but with no changes + diff => + { + // RootLayout rerendered + Assert.Collection(diff.Edits, + edit => + { + // Removed old page + Assert.Equal(RenderTreeEditType.RemoveFrame, edit.Type); + Assert.Equal(1, edit.SiblingIndex); + }, + edit => + { + // Inserted new nested layout + Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); + Assert.Equal(1, edit.SiblingIndex); + AssertFrame.Component( + batch.ReferenceFrames[edit.ReferenceFrameIndex]); + }); + }, + diff => + { + // New nested layout rendered + var edit = diff.Edits[1]; + Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); + AssertFrame.Component( + batch.ReferenceFrames[edit.ReferenceFrameIndex]); + }, + diff => + { + // New inner page rendered + var singleEdit = diff.Edits.Single(); + Assert.Equal(RenderTreeEditType.PrependFrame, singleEdit.Type); + AssertFrame.Text( + batch.ReferenceFrames[singleEdit.ReferenceFrameIndex], + $"{nameof(ComponentWithNestedLayout)} is here."); + }); + } + + private class RootLayout : AutoRenderComponent, ILayoutComponent + { + public RenderFragment Body { get; set; } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.AddContent(0, "RootLayout starts here"); + builder.AddContent(1, Body); + builder.AddContent(2, "RootLayout ends here"); + } + } + + [Layout(typeof(RootLayout))] + private class NestedLayout : AutoRenderComponent, ILayoutComponent + { + public RenderFragment Body { get; set; } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.AddContent(0, "NestedLayout starts here"); + builder.AddContent(1, Body); + builder.AddContent(2, "NestedLayout ends here"); + } + } + + [Layout(typeof(RootLayout))] + private class ComponentWithLayout : AutoRenderComponent + { + protected override void BuildRenderTree(RenderTreeBuilder builder) + => builder.AddContent(0, $"{nameof(ComponentWithLayout)} is here."); + } + + [Layout(typeof(RootLayout))] + private class DifferentComponentWithLayout : AutoRenderComponent + { + protected override void BuildRenderTree(RenderTreeBuilder builder) + => builder.AddContent(0, $"{nameof(DifferentComponentWithLayout)} is here."); + } + + [Layout(typeof(NestedLayout))] + private class ComponentWithNestedLayout : AutoRenderComponent + { + protected override void BuildRenderTree(RenderTreeBuilder builder) + => builder.AddContent(0, $"{nameof(ComponentWithNestedLayout)} is here."); + } + } +} diff --git a/test/shared/AssertFrame.cs b/test/shared/AssertFrame.cs index 1bc2cb9d12..ac320e0796 100644 --- a/test/shared/AssertFrame.cs +++ b/test/shared/AssertFrame.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using Microsoft.AspNetCore.Blazor.Components; using Microsoft.AspNetCore.Blazor.RenderTree; using Xunit;