aspnetcore/src/Components/test/E2ETest/ServerExecutionTests/PrerenderingTest.cs

198 lines
8.3 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using BasicTestApp;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
using Microsoft.AspNetCore.E2ETesting;
using OpenQA.Selenium;
using TestServer;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
{
[Collection("auth")] // Because auth uses cookies, this can't run in parallel with other auth tests
public class PrerenderingTest : ServerTestBase<BasicTestAppServerSiteFixture<PrerenderedStartup>>
{
public PrerenderingTest(
BrowserFixture browserFixture,
BasicTestAppServerSiteFixture<PrerenderedStartup> serverFixture,
ITestOutputHelper output)
: base(browserFixture, serverFixture, output)
{
}
[Fact]
public void CanTransitionFromPrerenderedToInteractiveMode()
{
Navigate("/prerendered/prerendered-transition");
// Prerendered output shows "not connected"
Browser.Equal("not connected", () => Browser.FindElement(By.Id("connected-state")).Text);
// Once connected, output changes
BeginInteractivity();
Browser.Equal("connected", () => Browser.FindElement(By.Id("connected-state")).Text);
// ... and now the counter works
Browser.FindElement(By.Id("increment-count")).Click();
Browser.Equal("1", () => Browser.FindElement(By.Id("count")).Text);
}
[Fact]
public void CanUseJSInteropFromOnAfterRenderAsync()
{
Navigate("/prerendered/prerendered-interop");
// Prerendered output can't use JSInterop
Browser.Equal("No value yet", () => Browser.FindElement(By.Id("val-get-by-interop")).Text);
Browser.Equal(string.Empty, () => Browser.FindElement(By.Id("val-set-by-interop")).GetAttribute("value"));
BeginInteractivity();
// Once connected, we can
Browser.Equal("Hello from interop call", () => Browser.FindElement(By.Id("val-get-by-interop")).Text);
Browser.Equal("Hello from interop call", () => Browser.FindElement(By.Id("val-set-by-interop")).GetAttribute("value"));
}
[Fact]
public void IsCompatibleWithLazyLoadWebAssembly()
{
Navigate("/prerendered/WithLazyAssembly");
var button = Browser.FindElement(By.Id("use-package-button"));
button.Click();
AssertLogDoesNotContainCriticalMessages("Could not load file or assembly 'Newtonsoft.Json");
}
[Fact]
public void CanInfluenceHeadDuringPrerender()
{
Navigate("/prerendered/prerendered-head");
var metaWithBindings = Browser.FindElement(By.Id("meta-with-bindings"));
var metaNoBindings = Browser.FindElement(By.Id("meta-no-bindings"));
// Validate updated head during prerender
Browser.Equal("Initial title", () => Browser.Title);
Browser.Equal("Initial meta content", () => metaWithBindings.GetAttribute("content"));
Browser.Equal("Immutable meta content", () => metaNoBindings.GetAttribute("content"));
BeginInteractivity();
// Wait for elements to be recreated with internal ids to permit mutation
metaWithBindings = WaitForNewElement(metaWithBindings, "meta-with-bindings");
metaNoBindings = WaitForNewElement(metaNoBindings, "meta-no-bindings");
// Validate updated head after prerender
Browser.Equal("Initial title", () => Browser.Title);
Browser.Equal("Initial meta content", () => metaWithBindings.GetAttribute("content"));
Browser.Equal("Immutable meta content", () => metaNoBindings.GetAttribute("content"));
// Change parameter of meta component
var inputMetaBinding = Browser.FindElement(By.Id("input-meta-binding"));
inputMetaBinding.Clear();
inputMetaBinding.SendKeys("Updated meta content\n");
// Wait for meta tag to be recreated with new attributes
metaWithBindings = WaitForNewElement(metaWithBindings, "meta-with-bindings");
// Validate new meta content attribute
Browser.Equal("Updated meta content", () => metaWithBindings.GetAttribute("content"));
IWebElement WaitForNewElement(IWebElement existingElement, string id)
{
var newElement = existingElement;
Browser.NotEqual(existingElement, () => newElement = Browser.FindElement(By.Id(id)) ?? newElement);
return newElement;
}
}
[Fact]
public void CanReadUrlHashOnlyOnceConnected()
{
var urlWithoutHash = "prerendered/show-uri?my=query&another=value";
var url = $"{urlWithoutHash}#some/hash?tokens";
// The server doesn't receive the hash part of the URL, so you can't
// read it during prerendering
Navigate(url);
Browser.Equal(
_serverFixture.RootUri + urlWithoutHash,
() => Browser.FindElement(By.TagName("strong")).Text);
// Once connected, you do have access to the full URL
BeginInteractivity();
Browser.Equal(
_serverFixture.RootUri + url,
() => Browser.FindElement(By.TagName("strong")).Text);
}
[Theory]
[InlineData("base/relative", "prerendered/base/relative")]
[InlineData("/root/relative", "/root/relative")]
[InlineData("http://absolute/url", "http://absolute/url")]
public async Task CanRedirectDuringPrerendering(string destinationParam, string expectedRedirectionLocation)
{
var requestUri = new Uri(
_serverFixture.RootUri,
"prerendered/prerendered-redirection?destination=" + destinationParam);
var httpClient = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false });
var response = await httpClient.GetAsync(requestUri);
var expectedUri = new Uri(_serverFixture.RootUri, expectedRedirectionLocation);
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal(expectedUri, response.Headers.Location);
}
[Theory]
[InlineData(null, null)]
[InlineData(null, "Bert")]
[InlineData("Bert", null)]
[InlineData("Bert", "Treb")]
public void CanAccessAuthenticationStateDuringStaticPrerendering(string initialUsername, string interactiveUsername)
{
// See that the authentication state is usable during the initial prerendering
SignInAs(initialUsername, null);
Navigate("/prerendered/prerendered-transition");
Browser.Equal($"Hello, {initialUsername ?? "anonymous"}!", () => Browser.FindElement(By.TagName("h1")).Text);
// See that during connection, we update to whatever the latest authentication state now is
SignInAs(interactiveUsername, null, useSeparateTab: true);
BeginInteractivity();
Browser.Equal($"Hello, {interactiveUsername ?? "anonymous"}!", () => Browser.FindElement(By.TagName("h1")).Text);
}
private void BeginInteractivity()
{
Browser.FindElement(By.Id("load-boot-script")).Click();
}
private void AssertLogDoesNotContainCriticalMessages(params string[] messages)
{
var log = Browser.Manage().Logs.GetLog(LogType.Browser);
foreach (var message in messages)
{
Assert.DoesNotContain(log, entry =>
{
return entry.Level == LogLevel.Severe
&& entry.Message.Contains(message);
});
}
}
private void SignInAs(string userName, string roles, bool useSeparateTab = false) =>
Browser.SignInAs(new Uri(_serverFixture.RootUri, "/prerendered/"), userName, roles, useSeparateTab);
}
}