Add unit tests for illegal ParameterView access

This commit is contained in:
Steve Sanderson 2019-09-27 16:21:27 +01:00 committed by Artak
parent 2fc604145a
commit 206f793fb5
2 changed files with 80 additions and 0 deletions

View File

@ -354,6 +354,40 @@ namespace Microsoft.AspNetCore.Components.Test
Assert.Equal("The value of IsFixed cannot be changed dynamically.", ex.Message);
}
[Fact]
public void ParameterViewSuppliedWithCascadingParametersCannotBeUsedAfterSynchronousReturn()
{
// Arrange
var providedValue = "Initial value";
var renderer = new TestRenderer();
var component = new TestComponent(builder =>
{
builder.OpenComponent<CascadingValue<string>>(0);
builder.AddAttribute(1, "Value", providedValue);
builder.AddAttribute(2, "ChildContent", new RenderFragment(childBuilder =>
{
childBuilder.OpenComponent<CascadingParameterConsumerComponent<string>>(0);
childBuilder.CloseComponent();
}));
builder.CloseComponent();
});
// Initial render; capture nested component
var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender();
var firstBatch = renderer.Batches.Single();
var nestedComponent = FindComponent<CascadingParameterConsumerComponent<string>>(firstBatch, out var nestedComponentId);
// Re-render CascadingValue with new value, so it gets a new ParameterView
providedValue = "Updated value";
component.TriggerRender();
Assert.Equal(2, renderer.Batches.Count);
// It's no longer able to access anything in the ParameterView it just received
var ex = Assert.Throws<InvalidOperationException>(nestedComponent.AttemptIllegalAccessToLastParameterView);
Assert.Equal("blah", ex.Message);
}
private static T FindComponent<T>(CapturedBatch batch, out int componentId)
{
var componentFrame = batch.ReferenceFrames.Single(
@ -378,6 +412,8 @@ namespace Microsoft.AspNetCore.Components.Test
class CascadingParameterConsumerComponent<T> : AutoRenderComponent
{
private ParameterView lastParameterView;
public int NumSetParametersCalls { get; private set; }
public int NumRenders { get; private set; }
@ -386,6 +422,7 @@ namespace Microsoft.AspNetCore.Components.Test
public override async Task SetParametersAsync(ParameterView parameters)
{
lastParameterView = parameters;
NumSetParametersCalls++;
await base.SetParametersAsync(parameters);
}
@ -395,6 +432,13 @@ namespace Microsoft.AspNetCore.Components.Test
NumRenders++;
builder.AddContent(0, $"CascadingParameter={CascadingParameter}; RegularParameter={RegularParameter}");
}
public void AttemptIllegalAccessToLastParameterView()
{
// You're not allowed to hold onto a ParameterView and access it later,
// so this should throw
lastParameterView.TryGetValue<object>("anything", out _);
}
}
class SecondCascadingParameterConsumerComponent<T1, T2> : CascadingParameterConsumerComponent<T1>

View File

@ -3701,6 +3701,24 @@ namespace Microsoft.AspNetCore.Components.Test
Assert.Contains("Cannot start a batch when one is already in progress.", ex.Message);
}
[Fact]
public void CannotAccessParameterViewAfterSynchronousReturn()
{
// Arrange
var renderer = new TestRenderer();
var component = new ParameterViewIllegalCapturingComponent();
var componentId = renderer.AssignRootComponentId(component);
renderer.RenderRootComponentAsync(componentId);
// Act/Assert
var ex = Assert.Throws<InvalidOperationException>(() =>
{
// TODO: check other types of access too
component.CapturedParameterView.TryGetValue<object>("anything", out _);
});
Assert.Equal("blah", ex.Message);
}
private class NoOpRenderer : Renderer
{
public NoOpRenderer() : base(new TestServiceProvider(), NullLoggerFactory.Instance)
@ -4443,5 +4461,23 @@ namespace Microsoft.AspNetCore.Components.Test
public new void ProcessPendingRender()
=> base.ProcessPendingRender();
}
class ParameterViewIllegalCapturingComponent : IComponent
{
public ParameterView CapturedParameterView { get; private set; }
public void Attach(RenderHandle renderHandle)
{
}
public Task SetParametersAsync(ParameterView parameters)
{
CapturedParameterView = parameters;
// Return a task that never completes to show that access is forbidded
// after the synchronous return, not just after the returned task completes
return new TaskCompletionSource<object>().Task;
}
}
}
}