// 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 BasicTestApp; using BasicTestApp.RouterTest; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; using Microsoft.AspNetCore.E2ETesting; using Microsoft.AspNetCore.Testing; using OpenQA.Selenium; using OpenQA.Selenium.Support.UI; using Xunit; using Xunit.Abstractions; namespace Microsoft.AspNetCore.Components.E2ETest.Tests { public class WebAssemblyLazyLoadTest : ServerTestBase> { public WebAssemblyLazyLoadTest( BrowserFixture browserFixture, ToggleExecutionModeServerFixture serverFixture, ITestOutputHelper output) : base(browserFixture, serverFixture, output) { } protected override void InitializeAsyncCore() { Navigate(ServerPathBase, noReload: false); Browser.MountTestComponent(); Browser.Exists(By.Id("blazor-error-ui")); var errorUi = Browser.FindElement(By.Id("blazor-error-ui")); Assert.Equal("none", errorUi.GetCssValue("display")); } [Fact] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/23856")] public void CanLazyLoadOnRouteChange() { // Navigate to a page without any lazy-loaded dependencies SetUrlViaPushState("/"); var app = Browser.MountTestComponent(); // Ensure that we haven't requested the lazy loaded assembly Assert.False(HasLoadedAssembly("Newtonsoft.Json.dll")); // Visit the route for the lazy-loaded assembly SetUrlViaPushState("/WithLazyAssembly"); var button = app.FindElement(By.Id("use-package-button")); // Now we should have requested the DLL Assert.True(HasLoadedAssembly("Newtonsoft.Json.dll")); button.Click(); // We shouldn't get any errors about assemblies not being available AssertLogDoesNotContainCriticalMessages("Could not load file or assembly 'Newtonsoft.Json"); } [Fact] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/23856")] public void CanLazyLoadOnFirstVisit() { // Navigate to a page with lazy loaded assemblies for the first time SetUrlViaPushState("/WithLazyAssembly"); var app = Browser.MountTestComponent(); // Wait for the page to finish loading new WebDriverWait(Browser, TimeSpan.FromSeconds(2)).Until( driver => driver.FindElement(By.Id("use-package-button")) != null); var button = app.FindElement(By.Id("use-package-button")); // We should have requested the DLL Assert.True(HasLoadedAssembly("Newtonsoft.Json.dll")); button.Click(); // We shouldn't get any errors about assemblies not being available AssertLogDoesNotContainCriticalMessages("Could not load file or assembly 'Newtonsoft.Json"); } [Fact] public void CanLazyLoadAssemblyWithRoutes() { // Navigate to a page without any lazy-loaded dependencies SetUrlViaPushState("/"); var app = Browser.MountTestComponent(); // Ensure that we haven't requested the lazy loaded assembly Assert.False(HasLoadedAssembly("LazyTestContentPackage.dll")); // Navigate to the designated route SetUrlViaPushState("/WithLazyLoadedRoutes"); // Wait for the page to finish loading new WebDriverWait(Browser, TimeSpan.FromSeconds(2)).Until( driver => driver.FindElement(By.Id("lazy-load-msg")) != null); // Now the assembly has been loaded Assert.True(HasLoadedAssembly("LazyTestContentPackage.dll")); var button = app.FindElement(By.Id("go-to-lazy-route")); button.Click(); // Navigating the lazy-loaded route should show its content var renderedElement = app.FindElement(By.Id("lazy-page")); Assert.True(renderedElement.Displayed); } [Fact] public void ThrowsErrorForUnavailableAssemblies() { // Navigate to a page with lazy loaded assemblies for the first time SetUrlViaPushState("/Other"); var app = Browser.MountTestComponent(); // Should've thrown an error for unhandled error var errorUiElem = Browser.Exists(By.Id("blazor-error-ui"), TimeSpan.FromSeconds(10)); Assert.NotNull(errorUiElem); AssertLogContainsCriticalMessages("DoesNotExist.dll must be marked with 'BlazorWebAssemblyLazyLoad' item group in your project file to allow lazy-loading."); } private string SetUrlViaPushState(string relativeUri) { var pathBaseWithoutHash = ServerPathBase.Split('#')[0]; var jsExecutor = (IJavaScriptExecutor)Browser; var absoluteUri = new Uri(_serverFixture.RootUri, $"{pathBaseWithoutHash}{relativeUri}"); jsExecutor.ExecuteScript($"Blazor.navigateTo('{absoluteUri.ToString().Replace("'", "\\'")}')"); return absoluteUri.AbsoluteUri; } private bool HasLoadedAssembly(string name) { var checkScript = $"return window.performance.getEntriesByType('resource').some(r => r.name.endsWith('{name}'));"; var jsExecutor = (IJavaScriptExecutor)Browser; var nameRequested = jsExecutor.ExecuteScript(checkScript); if (nameRequested != null) { return (bool)nameRequested; } return false; } 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); }); } } void AssertLogContainsCriticalMessages(params string[] messages) { var log = Browser.Manage().Logs.GetLog(LogType.Browser); foreach (var message in messages) { Assert.Contains(log, entry => { return entry.Level == LogLevel.Severe && entry.Message.Contains(message); }); } } } }