Skip to content

Commit

Permalink
Add E2E tests
Browse files Browse the repository at this point in the history
  • Loading branch information
javiercn committed Jan 26, 2024
1 parent 1c9ce21 commit 98298e4
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 8 deletions.
4 changes: 2 additions & 2 deletions src/Components/Server/src/Builder/InternalServerRenderMode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Microsoft.AspNetCore.Components.Server;

internal class InternalServerRenderMode(ServerComponentsEndpointOptions options = null) : InteractiveServerRenderMode
internal class InternalServerRenderMode(ServerComponentsEndpointOptions options) : InteractiveServerRenderMode
{
public ServerComponentsEndpointOptions? Options { get; } = options ?? new() { EnableWebSocketCompression = true };
public ServerComponentsEndpointOptions? Options { get; } = options;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class ServerComponentsEndpointOptions
/// <summary>
/// Gets or sets a value that indicates whether compression is enabled for the WebSocket connections.
/// </summary>
public bool EnableWebSocketCompression { get; set; }
public bool EnableWebSocketCompression { get; set; } = true;

/// <summary>
/// Gets or sets the <c>frame-ancestors</c> <c>Content-Security-Policy</c> to set in the
Expand All @@ -41,7 +41,8 @@ public class ServerComponentsEndpointOptions

/// <summary>
/// Gets or sets a callback to configure the underlying <see cref="HttpConnectionDispatcherOptions"/>.
/// If set, this callback takes precedence over <see cref="EnableWebSocketCompression"/>.
/// If set, <see cref="ContentSecurityFrameAncestorPolicy"/> will be applied independent of the value of
/// <see cref="EnableWebSocketCompression"/>.
/// </summary>
public Action<HttpConnectionDispatcherOptions> ConnectionOptions { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static RazorComponentsEndpointConventionBuilder AddInteractiveServerRende

ComponentEndpointConventionBuilderHelper.AddRenderMode(builder, new InternalServerRenderMode(options));

if (options.EnableWebSocketCompression && options.ContentSecurityFrameAncestorPolicy != null)
if ((options.EnableWebSocketCompression || options.ConnectionOptions is not null) && options.ContentSecurityFrameAncestorPolicy != null)
{
builder.AddEndpointFilter(new RequireCspFilter(options.ContentSecurityFrameAncestorPolicy));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Reflection;
using Microsoft.AspNetCore.E2ETesting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
Expand All @@ -17,6 +18,8 @@ public class AspNetSiteServerFixture : WebHostServerFixture

public BuildWebHost BuildWebHostMethod { get; set; }

public Action<IServiceProvider> UpdateHostServices { get; set; }

public GetContentRoot GetContentRootMethod { get; set; } = DefaultGetContentRoot;

public AspNetEnvironment Environment { get; set; } = AspNetEnvironment.Production;
Expand All @@ -40,12 +43,16 @@ protected override IHost CreateWebHost()
host = E2ETestOptions.Instance.Sauce.HostName;
}

return BuildWebHostMethod(new[]
var result = BuildWebHostMethod(new[]
{
"--urls", $"http://{host}:0",
"--contentroot", sampleSitePath,
"--environment", Environment.ToString(),
}.Concat(AdditionalArguments).ToArray());

UpdateHostServices?.Invoke(result.Services);

return result;
}

private static string DefaultGetContentRoot(Assembly assembly)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.RegularExpressions;
using Components.TestServer.RazorComponents;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
using Microsoft.AspNetCore.E2ETesting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestPlatform.Utilities;
using OpenQA.Selenium;
using TestServer;
using Xunit.Abstractions;

namespace Microsoft.AspNetCore.Components.E2ETests.ServerExecutionTests;

public abstract partial class AllowedWebSocketCompressionTests(
BrowserFixture browserFixture,
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture,
ITestOutputHelper output)
: ServerTestBase<BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>>>(browserFixture, serverFixture, output)
{
[Fact]
public void EmbeddingServerAppInsideIframe_Works()
{
Navigate("/subdir/iframe");

var logs = Browser.GetBrowserLogs(LogLevel.Severe);

Assert.Empty(logs);

// Get the iframe element from the page, and inspect its contents for a p element with id inside-iframe
var iframe = Browser.FindElement(By.TagName("iframe"));
Browser.SwitchTo().Frame(iframe);
Browser.Exists(By.Id("inside-iframe"));
}
}

public abstract partial class BlockedWebSocketCompressionTests(
BrowserFixture browserFixture,
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture,
ITestOutputHelper output)
: ServerTestBase<BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>>>(browserFixture, serverFixture, output)
{
[Fact]
public void EmbeddingServerAppInsideIframe_WithCompressionEnabled_Fails()
{
Navigate("/subdir/iframe");

var logs = Browser.GetBrowserLogs(LogLevel.Severe);

Assert.True(logs.Count > 0);

Assert.Matches(ParseErrorMessage(), logs[0].Message);
}

[GeneratedRegex(@"security - Refused to frame 'http://\d+\.\d+\.\d+\.\d+:\d+/' because an ancestor violates the following Content Security Policy directive: ""frame-ancestors 'none'"".")]
private static partial Regex ParseErrorMessage();
}

public partial class DefaultConfigurationWebSocketCompressionTests(
BrowserFixture browserFixture,
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture,
ITestOutputHelper output)
: BlockedWebSocketCompressionTests(browserFixture, serverFixture, output)
{
}

public partial class CustomConfigurationCallbackWebSocketCompressionTests : BlockedWebSocketCompressionTests
{
public CustomConfigurationCallbackWebSocketCompressionTests(
BrowserFixture browserFixture,
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture,
ITestOutputHelper output) : base(browserFixture, serverFixture, output)
{
serverFixture.UpdateHostServices = services =>
{
var configuration = services.GetService<WebSocketCompressionConfiguration>();
configuration.ConnectionDispatcherOptions = options =>
options.WebSockets.WebSocketAcceptContextFactory = context =>
new Http.WebSocketAcceptContext { DangerousEnableCompression = true };
};
}
}

public partial class CompressionDisabledWebSocketCompressionTests : AllowedWebSocketCompressionTests
{
public CompressionDisabledWebSocketCompressionTests(
BrowserFixture browserFixture,
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture,
ITestOutputHelper output) : base(
browserFixture, serverFixture, output)
{
serverFixture.UpdateHostServices = services =>
{
var configuration = services.GetService<WebSocketCompressionConfiguration>();
configuration.IsCompressionEnabled = false;
};
}
}

public partial class SelfFrameAncestorWebSocketCompressionTests : AllowedWebSocketCompressionTests
{
public SelfFrameAncestorWebSocketCompressionTests(
BrowserFixture browserFixture,
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture,
ITestOutputHelper output)
: base(browserFixture, serverFixture, output)
{
serverFixture.UpdateHostServices = services =>
{
var configuration = services.GetService<WebSocketCompressionConfiguration>();
configuration.CspPolicy = "'self'";
};
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public void ConfigureServices(IServiceCollection services)
{
services.AddRazorComponents()
.AddInteractiveServerComponents();
services.AddSingleton<ResourceRequestLog>();

// Since tests run in parallel, we use an ephemeral key provider to avoid filesystem
// contention issues.
Expand All @@ -42,7 +43,7 @@ public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env,
{
endpoints.MapRazorComponents<Root>()
.AddInteractiveWebAssemblyRenderMode()
.AddInteractiveServerRenderMode(options => options.EnableWebSocketCompression = true);
.AddInteractiveServerRenderMode();
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddHttpContextAccessor();
services.AddSingleton<AsyncOperationService>();
services.AddCascadingAuthenticationState();
services.AddSingleton<WebSocketCompressionConfiguration>();

var circuitContextAccessor = new TestCircuitContextAccessor();
services.AddSingleton<CircuitHandler>(circuitContextAccessor);
Expand Down Expand Up @@ -69,7 +70,13 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
endpoints.MapRazorComponents<TRootComponent>()
.AddAdditionalAssemblies(Assembly.Load("Components.WasmMinimal"))
.AddInteractiveServerRenderMode()
.AddInteractiveServerRenderMode(options =>
{
var config = app.ApplicationServices.GetRequiredService<WebSocketCompressionConfiguration>();
options.EnableWebSocketCompression = config.IsCompressionEnabled;
options.ContentSecurityFrameAncestorPolicy = config.CspPolicy;
options.ConnectionOptions = config.ConnectionDispatcherOptions;
})
.AddInteractiveWebAssemblyRenderMode(options => options.PathPrefix = "/WasmMinimal");

NotEnabledStreamingRenderingComponent.MapEndpoints(endpoints);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@page "/iframe"

<iframe src="embedded"></iframe>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@page "/embedded"
@rendermode Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer

<p id="inside-iframe">This is some content embedded inside an iframe</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Http.Connections;

namespace TestServer;

public class WebSocketCompressionConfiguration
{
public bool IsCompressionEnabled { get; set; } = true;

public string CspPolicy { get; set; } = "'none'";

public Action<HttpConnectionDispatcherOptions> ConnectionDispatcherOptions { get; set; }
}

0 comments on commit 98298e4

Please sign in to comment.