Add unit tests for illegal ParameterView access
This commit is contained in:
parent
2fc604145a
commit
206f793fb5
|
|
@ -354,6 +354,40 @@ namespace Microsoft.AspNetCore.Components.Test
|
||||||
Assert.Equal("The value of IsFixed cannot be changed dynamically.", ex.Message);
|
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)
|
private static T FindComponent<T>(CapturedBatch batch, out int componentId)
|
||||||
{
|
{
|
||||||
var componentFrame = batch.ReferenceFrames.Single(
|
var componentFrame = batch.ReferenceFrames.Single(
|
||||||
|
|
@ -378,6 +412,8 @@ namespace Microsoft.AspNetCore.Components.Test
|
||||||
|
|
||||||
class CascadingParameterConsumerComponent<T> : AutoRenderComponent
|
class CascadingParameterConsumerComponent<T> : AutoRenderComponent
|
||||||
{
|
{
|
||||||
|
private ParameterView lastParameterView;
|
||||||
|
|
||||||
public int NumSetParametersCalls { get; private set; }
|
public int NumSetParametersCalls { get; private set; }
|
||||||
public int NumRenders { 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)
|
public override async Task SetParametersAsync(ParameterView parameters)
|
||||||
{
|
{
|
||||||
|
lastParameterView = parameters;
|
||||||
NumSetParametersCalls++;
|
NumSetParametersCalls++;
|
||||||
await base.SetParametersAsync(parameters);
|
await base.SetParametersAsync(parameters);
|
||||||
}
|
}
|
||||||
|
|
@ -395,6 +432,13 @@ namespace Microsoft.AspNetCore.Components.Test
|
||||||
NumRenders++;
|
NumRenders++;
|
||||||
builder.AddContent(0, $"CascadingParameter={CascadingParameter}; RegularParameter={RegularParameter}");
|
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>
|
class SecondCascadingParameterConsumerComponent<T1, T2> : CascadingParameterConsumerComponent<T1>
|
||||||
|
|
|
||||||
|
|
@ -3701,6 +3701,24 @@ namespace Microsoft.AspNetCore.Components.Test
|
||||||
Assert.Contains("Cannot start a batch when one is already in progress.", ex.Message);
|
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
|
private class NoOpRenderer : Renderer
|
||||||
{
|
{
|
||||||
public NoOpRenderer() : base(new TestServiceProvider(), NullLoggerFactory.Instance)
|
public NoOpRenderer() : base(new TestServiceProvider(), NullLoggerFactory.Instance)
|
||||||
|
|
@ -4443,5 +4461,23 @@ namespace Microsoft.AspNetCore.Components.Test
|
||||||
public new void ProcessPendingRender()
|
public new void ProcessPendingRender()
|
||||||
=> base.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue