Account for Layout ordering in Blazor (#15001)
The server requires that clients send descriptors in sequence. Since MVC executes Layouts in an inside-out manner, modify the client to explicitly order descriptors Fixes https://github.com/aspnet/AspNetCore/issues/14474
This commit is contained in:
parent
579f30f591
commit
30b31d7086
|
|
@ -3,7 +3,7 @@
|
|||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
"Microsoft": "Debug"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -101,7 +101,7 @@ export function discoverComponents(document: Document): ComponentDescriptor[] {
|
|||
discoveredComponents.push(entry);
|
||||
}
|
||||
|
||||
return discoveredComponents;
|
||||
return discoveredComponents.sort((a, b) => a.sequence - b.sequence);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ using Xunit.Abstractions;
|
|||
|
||||
namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
||||
{
|
||||
public class MultipleComponentsTest : ServerTestBase<BasicTestAppServerSiteFixture<PrerenderedStartup>>
|
||||
public class MultipleComponentsTest : ServerTestBase<BasicTestAppServerSiteFixture<MultipleComponents>>
|
||||
{
|
||||
private const string MarkerPattern = ".*?<!--Blazor:(.*?)-->.*?";
|
||||
|
||||
public MultipleComponentsTest(
|
||||
BrowserFixture browserFixture,
|
||||
BasicTestAppServerSiteFixture<PrerenderedStartup> serverFixture,
|
||||
BasicTestAppServerSiteFixture<MultipleComponents> serverFixture,
|
||||
ITestOutputHelper output)
|
||||
: base(browserFixture, serverFixture, output)
|
||||
{
|
||||
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
[Fact]
|
||||
public void DoesNotStartMultipleConnections()
|
||||
{
|
||||
Navigate("/prerendered/multiple-components");
|
||||
Navigate("/multiple-components");
|
||||
|
||||
BeginInteractivity();
|
||||
Browser.Exists(By.CssSelector("h3.interactive"));
|
||||
|
|
@ -60,17 +60,18 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void CanRenderMultipleRootComponents()
|
||||
{
|
||||
Navigate("/prerendered/multiple-components");
|
||||
Navigate("/multiple-components");
|
||||
|
||||
var greets = Browser.FindElements(By.CssSelector(".greet-wrapper .greet")).Select(e => e.Text).ToArray();
|
||||
|
||||
Assert.Equal(5, greets.Length); // 1 statically rendered + 3 prerendered + 1 server prerendered
|
||||
Assert.Equal(7, greets.Length); // 1 statically rendered + 5 prerendered + 1 server prerendered
|
||||
Assert.DoesNotContain("Hello Red fish", greets);
|
||||
Assert.Single(greets, "Hello John");
|
||||
Assert.Single(greets, "Hello Abraham");
|
||||
Assert.Equal(2, greets.Where(g => g == "Hello Blue fish").Count());
|
||||
Assert.Equal(3, greets.Where(g => string.Equals("Hello", g)).Count()); // 3 server prerendered without parameters
|
||||
var content = Browser.FindElement(By.Id("test-container")).GetAttribute("innerHTML");
|
||||
var markers = ReadMarkers(content);
|
||||
|
|
@ -86,7 +87,11 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
false,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
};
|
||||
Assert.Equal(expectedComponentSequence, componentSequence);
|
||||
|
||||
|
|
@ -96,6 +101,8 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
Browser.Exists(By.CssSelector("h3.interactive"));
|
||||
var updatedGreets = Browser.FindElements(By.CssSelector(".greet-wrapper .greet")).Select(e => e.Text).ToArray();
|
||||
Assert.Equal(7, updatedGreets.Where(g => string.Equals("Hello Alfred", g)).Count());
|
||||
Assert.Equal(2, updatedGreets.Where(g => g == "Hello Red fish").Count());
|
||||
Assert.Equal(2, updatedGreets.Where(g => g == "Hello Blue fish").Count());
|
||||
Assert.Single(updatedGreets.Where(g => string.Equals("Hello Albert", g)));
|
||||
Assert.Single(updatedGreets.Where(g => string.Equals("Hello Abraham", g)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace TestServer
|
||||
{
|
||||
public class MultipleComponents
|
||||
{
|
||||
public MultipleComponents(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc();
|
||||
services.AddServerSideBlazor();
|
||||
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.Map("/multiple-components", app =>
|
||||
{
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseRouting();
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapRazorPages();
|
||||
endpoints.MapFallbackToPage("/MultipleComponents");
|
||||
endpoints.MapBlazorHub();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +1,29 @@
|
|||
@page "/multiple-components"
|
||||
@using BasicTestApp.MultipleComponents;
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Multiple component entry points</title>
|
||||
<base href="~/" />
|
||||
@* This page is used to validate the ability to render multiple root components in a blazor server-side application.
|
||||
*@
|
||||
</head>
|
||||
<body>
|
||||
<div id="test-container">
|
||||
@(await Html.RenderComponentAsync<GreeterComponent>(RenderMode.ServerPrerendered))
|
||||
@(await Html.RenderComponentAsync<GreeterComponent>(RenderMode.Server))
|
||||
<component type="typeof(GreeterComponent)" render-mode="Static" param-name='"John"' />
|
||||
<component type="typeof(GreeterComponent)" render-mode="Server"/>
|
||||
<div id="container">
|
||||
<p>Some content before</p>
|
||||
<component type="typeof(GreeterComponent)" render-mode="Server"/>
|
||||
<p>Some content between</p>
|
||||
<component type="typeof(GreeterComponent)" render-mode="ServerPrerendered"/>
|
||||
<p>Some content after</p>
|
||||
<div id="nested-an-extra-level">
|
||||
<p>Some content before</p>
|
||||
<component type="typeof(GreeterComponent)" render-mode="Server"/>
|
||||
<component type="typeof(GreeterComponent)" render-mode="ServerPrerendered"/>
|
||||
<p>Some content after</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="container">
|
||||
<component type="typeof(GreeterComponent)" render-mode="Server" param-name='"Albert"' />
|
||||
<component type="typeof(GreeterComponent)" render-mode="ServerPrerendered" param-name='"Abraham"' />
|
||||
</div>
|
||||
|
||||
@{
|
||||
Layout = "./MultipleComponentsLayout.cshtml";
|
||||
}
|
||||
|
||||
|
||||
@(await Html.RenderComponentAsync<GreeterComponent>(RenderMode.ServerPrerendered))
|
||||
@(await Html.RenderComponentAsync<GreeterComponent>(RenderMode.Server))
|
||||
<component type="typeof(GreeterComponent)" render-mode="Static" param-name='"John"' />
|
||||
<component type="typeof(GreeterComponent)" render-mode="Server" />
|
||||
<div id="container">
|
||||
<p>Some content before</p>
|
||||
<component type="typeof(GreeterComponent)" render-mode="Server" />
|
||||
<p>Some content between</p>
|
||||
<component type="typeof(GreeterComponent)" render-mode="ServerPrerendered" />
|
||||
<p>Some content after</p>
|
||||
<div id="nested-an-extra-level">
|
||||
<p>Some content before</p>
|
||||
<component type="typeof(GreeterComponent)" render-mode="Server" />
|
||||
<component type="typeof(GreeterComponent)" render-mode="ServerPrerendered" />
|
||||
<p>Some content after</p>
|
||||
</div>
|
||||
|
||||
@*
|
||||
So that E2E tests can make assertions about both the prerendered and
|
||||
interactive states, we only load the .js file when told to.
|
||||
*@
|
||||
<hr />
|
||||
|
||||
<button id="load-boot-script" onclick="start()">Load boot script</button>
|
||||
|
||||
<script src="_framework/blazor.server.js" autostart="false"></script>
|
||||
<script>
|
||||
// Used by InteropOnInitializationComponent
|
||||
function setElementValue(element, newValue) {
|
||||
element.value = newValue;
|
||||
return element.value;
|
||||
}
|
||||
|
||||
function start() {
|
||||
Blazor.start({
|
||||
logLevel: 1 // LogLevel.Debug
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
<div id="container">
|
||||
<component type="typeof(GreeterComponent)" render-mode="Server" param-name='"Albert"' />
|
||||
<component type="typeof(GreeterComponent)" render-mode="ServerPrerendered" param-name='"Abraham"' />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
@using BasicTestApp.MultipleComponents;
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Multiple component entry points</title>
|
||||
<base href="~/" />
|
||||
@* This page is used to validate the ability to render multiple root components in a blazor server-side application.
|
||||
*@
|
||||
</head>
|
||||
<body>
|
||||
<div id="test-container">
|
||||
<component type="typeof(GreeterComponent)" render-mode="Server" param-name='"Red fish"' />
|
||||
<component type="typeof(GreeterComponent)" render-mode="ServerPrerendered" param-name='"Blue fish"' />
|
||||
@RenderBody()
|
||||
<component type="typeof(GreeterComponent)" render-mode="Server" param-name='"Red fish"' />
|
||||
<component type="typeof(GreeterComponent)" render-mode="ServerPrerendered" param-name='"Blue fish"' />
|
||||
</div>
|
||||
|
||||
@*
|
||||
So that E2E tests can make assertions about both the prerendered and
|
||||
interactive states, we only load the .js file when told to.
|
||||
*@
|
||||
<hr />
|
||||
|
||||
<button id="load-boot-script" onclick="start()">Load boot script</button>
|
||||
|
||||
<script src="_framework/blazor.server.js" autostart="false"></script>
|
||||
<script>
|
||||
// Used by InteropOnInitializationComponent
|
||||
function setElementValue(element, newValue) {
|
||||
element.value = newValue;
|
||||
return element.value;
|
||||
}
|
||||
|
||||
function start() {
|
||||
Blazor.start({
|
||||
logLevel: 1 // LogLevel.Debug
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,15 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -28,6 +23,7 @@ namespace TestServer
|
|||
["Server authentication"] = (BuildWebHost<ServerAuthenticationStartup>(CreateAdditionalArgs(args)), "/subdir"),
|
||||
["CORS (WASM)"] = (BuildWebHost<CorsStartup>(CreateAdditionalArgs(args)), "/subdir"),
|
||||
["Prerendering (Server-side)"] = (BuildWebHost<PrerenderedStartup>(CreateAdditionalArgs(args)), "/prerendered"),
|
||||
["Multiple components (Server-side)"] = (BuildWebHost<MultipleComponents>(CreateAdditionalArgs(args)), "/multiple-components"),
|
||||
["Globalization + Localization (Server-side)"] = (BuildWebHost<InternationalizationStartup>(CreateAdditionalArgs(args)), "/subdir"),
|
||||
["Server-side blazor"] = (BuildWebHost<ServerStartup>(CreateAdditionalArgs(args)), "/subdir"),
|
||||
["Hosted client-side blazor"] = (BuildWebHost<ClientStartup>(CreateAdditionalArgs(args)), "/subdir"),
|
||||
|
|
|
|||
Loading…
Reference in New Issue