-
-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Render(RenderFragment)
doesn't re-render and has other limitations
#1289
Comments
I am not really surprised by this. A test component is not a real component, there is no render handler attached to it. Did a few experiments: @inherits TestContext
@code {
public RazorStuff(ITestOutputHelper output)
{
// uses the Meziantou.Extensions.Logging.Xunit package
Services.AddLogging(options =>
{
options.AddProvider(new XUnitLoggerProvider(output, new XUnitLoggerOptions
{
UseUtcTimestamp = true,
IncludeScopes = false,
IncludeCategory = true,
IncludeLogLevel = true,
TimestampFormat = "s"
}));
options.SetMinimumLevel(LogLevel.Trace);
});
}
[Fact]
public void ThatIsMyCoolTest()
{
var output = string.Empty;
var cut = (IRenderedComponent<IComponent>)Render(@<button @onclick=@(() => output = "Success")>@output</button>);
cut.Find("button").Click();
cut.Render();
Assert.Equal("Success", output); // passes
Assert.Equal("Success", cut.Find("button").TextContent); // fails
}
}
The log output shows that the event is processed:
This is the decompiled razor code: [Fact]
public void ThatIsMyCoolTest_decompiled()
{
var output = string.Empty;
var cut = (IRenderedComponent<IComponent>)Render((__builder2) =>
{
__builder2.OpenElement(0, "button");
__builder2.AddAttribute(1, "onclick", EventCallback.Factory.Create<MouseEventArgs>(this, () => output = "Success"));
__builder2.AddContent(2, output);
__builder2.CloseElement();
});
cut.Find("button").Click();
cut.Render();
Assert.Equal("Success", output);
Assert.Equal("Success", cut.Find("button").TextContent);
} |
Here are some more observations:
var cut = (IRenderedComponent<IComponent>)Render(@</>);
cut.Render(); The problem is that the public Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<RenderFragment>("ChildContent", out var childContent))
{
renderHandle.Render(childContent);
}
return Task.CompletedTask;
}
|
Yeah, but then again, I am not sure if this is actually a bug and even if it is something we can do something about. The examples we are discussing here is not really scenarios I find problematic that we don't support. Do we have test scenarios that we should support but don't? |
Well given the link from the user to SO - he wanted to test the two-way-binding of his custom component. |
hmm, but didn't that work? Declare a variable in the test, bind it in a component, trigger change, see variable update. The problem is/was that he also bound said variable to markup in the render fragment belonging to the test itself. [Test]
public void TestMyInputComponent()
{
var testModel = new Person();
var editCtx = new EditContext(testModel);
var cut = Render(
@<EditForm EditContext="editCtx">
<MyCustomInputComponent Label="Firstname" @bind-Value="testModel.Firstname"></MyCustomInputComponent>
</EditForm>);
var inputElement = cut.Find("input");
inputElement.Input("John");
// Passes
Assert.That(testModel.Firstname, Is.EqualTo("John"));
} This test verifies that two-way binding. That said, there certainly may be other reasons for needing to bind to markup declared outside components in a test. But I just can't think of a way we can do it. So I would not consider this a bug, but do think it is a good idea to note this limitation with a few examples in the docs. My comment in the PR with the docs update came about because I didn't understand the case it was trying to explain. |
While the test might work with the bound model - but it still might be confusing for users. |
Yeah, I guess it could. But that's because an external razor file is a component that is managed by the rendeeer, a test written in a .razor file is not a component, and it's definitely not managed by the renderer, and I don't see a way for it to be that. I think it would require the test to be inside a real component and that test would then have to be triggered through a custom test runner. I am honestly happy that variables/types declared in a test can be used to test two way binding as I show above. But still, I agree it may be surprising to some, thus I think an explainer in the docs is warranted. |
What is more suprising is that even simple
On your page here - just arguing from the users point of view. It is odd if you move the same code out from inside the test to a separate component - it behaves different. Furthermore, this two versions might behave different as well: @* If we are directly inheriting from ComponentBase and create the TestContext down below - the tests from the user would pass *@
@inherits TestContext
<button @onclick"...">Click Me</button>
@code {
[Fact]
public void Title()
{
var cut = RenderComponent<ThisTestCompoinent>();
cut.Find("button").Click();
}
} Just moving the code "up" does something else. And with That said - I reopened #1288 |
I see the ambiguity. My point is that people should not think of razor filled with unit tests in as components. They should just use it as a .cs file with the ability to have markup mixed together with c# inside methods. So we want to make it very clear that the template looks like this:
And that users should think of this as classes, not components. That should remove much if the ambiguity, I think. |
The current
Render(@</>)
function that accepts aRenderFragment
has some rather strange quirks and limitations.Here an example, that for sure should work:
In this case, no onclick event handler was found. Another example is in combination with
@bind
:The text was updated successfully, but these errors were encountered: