Component does not re-render if InputFile control included in Razor markup #318
-
Describe the bug Per the razor component below, simply add an Note that the For reference: Microsoft.AspnetCore.Components.Forms.InputFile Also, occasionally, I saw a Bunit recursive render stack overflow, but that seemed to be caused by the Example: @if (Forecasts == null)
{
<p id="LoadingMessage"><em>Loading...</em></p>
}
else
{
<InputFile Id="sampleInputFileIdbad" />
<input type="file" id="sampleInputFileId" />
<table id="forecasttable" class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in Forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
[Inject]
public HttpClient Http { get; set; }
public List<WeatherForecast> Forecasts;
protected override async Task OnInitializedAsync()
{
await LoadForecasts();
}
public async Task LoadForecasts()
{
var results = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
Forecasts = results.ToList();
}
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}
With this test: using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using BBINS.Shared;
using Bunit;
using RichardSzalay.MockHttp;
using Xunit;
namespace Client.Test
{
public class WeatherDisplayTests : TestContext
{
public WeatherDisplayTests()
{
var mock = Services.AddMockHttpClient();
mock.When("/sample-data/weather.json").RespondJson(new List<WeatherDisplay.WeatherForecast> {
new()
{
Date = new DateTime(1990, 12, 31),
Summary = "Freezing",
TemperatureC = 1
},
new()
{
Date = new DateTime(2001, 12, 31),
Summary = "Freezing",
TemperatureC = 2
}
});
}
[Fact]
public void WeatherDisplayInitializeTest()
{
var cut = RenderComponent<WeatherDisplay>();
// Is this a race condition?
Assert.Equal("Loading...", cut.Find("p#LoadingMessage").TextContent);
}
[Fact]
public void WeatherDisplayInitializeTest2()
{
var cut = RenderComponent<WeatherDisplay>();
cut.WaitForState(() => cut.Instance.Forecasts != null);
// Will not find the table if InputFile included in Razor
cut.WaitForAssertion(() => cut.Find("#forecasttable"));
}
}
} Results in this output:
Expected behavior:
Version info: Test Project <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="bunit.web" Version="1.0.0-preview-01" />
<PackageReference Include="bunit.xunit" Version="1.0.0-preview-01" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="1.3.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BBINS-bUnit\BBINS-bUnit.csproj" />
</ItemGroup>
</Project>
Version Info: Blazor WASM project <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<AssemblyName>BBINS</AssemblyName>
<RootNamespace>BBINS</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.2" PrivateAssets="all" />
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
</ItemGroup>
</Project>
|
Beta Was this translation helpful? Give feedback.
Replies: 6 comments 7 replies
-
Forgot to include project files: |
Beta Was this translation helpful? Give feedback.
-
First of, excellent bug report writing, thanks for adding all the details @mrakestraw-bbins 🥇🥇 I just did a read through the InputFile component, and it has some JSInterop magic going on, which might be causing the problems... I wonder if this is related to preview-01 adding support for This should be a fun one investigating. And just to clarify, when awaiting something that just returns a completed task immediately, the usage of |
Beta Was this translation helpful? Give feedback.
-
Thanks @egil. Nice library by the way. Wish I had more time to dig in and try to patch it myself. |
Beta Was this translation helpful? Give feedback.
-
This is probably related to this error that sometimes happens when running the tests here in GitHub Actions:
I know this test used to work all the time, so I will have to investigate where a possible regression has been introduced. Gotta love those subtle race condition bugs 😒 |
Beta Was this translation helpful? Give feedback.
-
@mrakestraw-bbins, if I reduce the test case even more, we get a different exception/error, which I actually thing is the root cause, because the error indicate a missing Our CUT: @if (!show)
{
<div id="loading"/>
}
else
{
<InputFile Id="sampleInputFileIdbad" />
<div id="showing"/>
}
@code {
bool show = false;
protected override async Task OnInitializedAsync()
{
await Task.CompletedTask;
show = true;
}
} The test: [Fact]
public async Task WeatherDisplayInitializeTest2()
{
var cut = RenderComponent<WeatherDisplay>();
Assert.Equal(1, cut.FindAll("#showing").Count);
} The output:
|
Beta Was this translation helpful? Give feedback.
-
OK, this turns out not to be a race condition or a bug, per say... but more a problem with bUnit not having built in support for the Here is how to write the test, using a mocking framework of choice, to create a mock of the using System;
using System.Collections.Generic;
using BBINS.Shared;
using Bunit;
using Bunit.JSInterop;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NSubstitute;
using RichardSzalay.MockHttp;
using Xunit;
namespace Client.Test
{
public class WeatherDisplayTests : TestContext
{
public WeatherDisplayTests()
{
var mock = Services.AddMockHttpClient();
mock.When("/sample-data/weather.json").RespondJson(new List<WeatherDisplay.WeatherForecast> {
new()
{
Date = new DateTime(1990, 12, 31),
Summary = "Freezing",
TemperatureC = 1
},
new()
{
Date = new DateTime(2001, 12, 31),
Summary = "Freezing",
TemperatureC = 2
}
});
var optionsMock = Substitute.For<IOptions<RemoteBrowserFileStreamOptions>>();
Services.AddSingleton<IOptions<RemoteBrowserFileStreamOptions>>(optionsMock);
JSInterop.SetupVoid("Blazor._internal.InputFile.init", x => true);
}
[Fact]
public void WeatherDisplayInitializeTest()
{
var cut = RenderComponent<WeatherDisplay>();
// Is this a race condition?
Assert.Equal("Loading...", cut.Find("p#LoadingMessage").TextContent);
}
[Fact]
public void WeatherDisplayInitializeTest2()
{
var cut = RenderComponent<WeatherDisplay>();
cut.WaitForState(() => cut.Instance.Forecasts != null);
// Will not find the table if InputFile included in Razor
cut.WaitForAssertion(() => cut.Find("#forecasttable"));
}
[Fact]
public void WeatherDisplayInitializeTest3()
{
var cut = RenderComponent<WeatherDisplay>();
cut.WaitForAssertion(() => cut.Find("#forecasttable"));
}
}
} |
Beta Was this translation helpful? Give feedback.
OK, this turns out not to be a race condition or a bug, per say... but more a problem with bUnit not having built in support for the
<InputFile>
component.Here is how to write the test, using a mocking framework of choice, to create a mock of the
IOptions<RemoteBrowserFileStreamOptions>
thatInputFile
requires.