156 lines
7.1 KiB
C#
156 lines
7.1 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.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using HostedInAspNet.Server;
|
|
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
|
|
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
|
|
using Microsoft.AspNetCore.E2ETesting;
|
|
using Microsoft.AspNetCore.Testing;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using OpenQA.Selenium;
|
|
using OpenQA.Selenium.Support.UI;
|
|
using Xunit;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
|
{
|
|
public class BootResourceCachingTest
|
|
: ServerTestBase<AspNetSiteServerFixture>
|
|
{
|
|
// The cache name is derived from the application's base href value (in this case, '/')
|
|
private const string CacheName = "blazor-resources-/";
|
|
|
|
public BootResourceCachingTest(
|
|
BrowserFixture browserFixture,
|
|
AspNetSiteServerFixture serverFixture,
|
|
ITestOutputHelper output)
|
|
: base(browserFixture, serverFixture, output)
|
|
{
|
|
serverFixture.BuildWebHostMethod = Program.BuildWebHost;
|
|
}
|
|
|
|
public override Task InitializeAsync()
|
|
{
|
|
return base.InitializeAsync(Guid.NewGuid().ToString());
|
|
}
|
|
|
|
[Fact]
|
|
[QuarantinedTest]
|
|
public void CachesResourcesAfterFirstLoad()
|
|
{
|
|
// On the first load, we have to fetch everything
|
|
Navigate("/");
|
|
WaitUntilLoaded();
|
|
var initialResourcesRequested = GetAndClearRequestedPaths();
|
|
Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith("/blazor.boot.json")));
|
|
Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith("/dotnet.wasm")));
|
|
Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith(".js")));
|
|
Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith(".dll")));
|
|
|
|
// On subsequent loads, we skip the items referenced from blazor.boot.json
|
|
// which includes .dll files and dotnet.wasm
|
|
Navigate("about:blank");
|
|
Navigate("/");
|
|
WaitUntilLoaded();
|
|
var subsequentResourcesRequested = GetAndClearRequestedPaths();
|
|
Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith("/blazor.boot.json")));
|
|
Assert.Empty(subsequentResourcesRequested.Where(path => path.EndsWith("/dotnet.wasm")));
|
|
Assert.NotEmpty(subsequentResourcesRequested.Where(path => path.EndsWith(".js")));
|
|
Assert.Empty(subsequentResourcesRequested.Where(path => path.EndsWith(".dll")));
|
|
}
|
|
|
|
[Fact]
|
|
[QuarantinedTest]
|
|
public void IncrementallyUpdatesCache()
|
|
{
|
|
// Perform a first load to populate the cache
|
|
Navigate("/");
|
|
WaitUntilLoaded();
|
|
var cacheEntryUrls1 = GetCacheEntryUrls();
|
|
var cacheEntryForComponentsDll = cacheEntryUrls1.Single(url => url.Contains("/Microsoft.AspNetCore.Components.dll"));
|
|
var cacheEntryForDotNetWasm = cacheEntryUrls1.Single(url => url.Contains("/dotnet.wasm"));
|
|
var cacheEntryForDotNetWasmWithChangedHash = cacheEntryForDotNetWasm.Replace(".sha256-", ".sha256-different");
|
|
|
|
// Remove some items we do need, and add an item we don't need
|
|
RemoveCacheEntry(cacheEntryForComponentsDll);
|
|
RemoveCacheEntry(cacheEntryForDotNetWasm);
|
|
AddCacheEntry(cacheEntryForDotNetWasmWithChangedHash, "ignored content");
|
|
var cacheEntryUrls2 = GetCacheEntryUrls();
|
|
Assert.DoesNotContain(cacheEntryForComponentsDll, cacheEntryUrls2);
|
|
Assert.DoesNotContain(cacheEntryForDotNetWasm, cacheEntryUrls2);
|
|
Assert.Contains(cacheEntryForDotNetWasmWithChangedHash, cacheEntryUrls2);
|
|
|
|
// On the next load, we'll fetch only the items we need (not things already cached)
|
|
GetAndClearRequestedPaths();
|
|
Navigate("about:blank");
|
|
Navigate("/");
|
|
WaitUntilLoaded();
|
|
var subsequentResourcesRequested = GetAndClearRequestedPaths();
|
|
Assert.Collection(subsequentResourcesRequested.Where(url => url.Contains(".dll")),
|
|
requestedDll => Assert.Contains("/Microsoft.AspNetCore.Components.dll", requestedDll));
|
|
Assert.Collection(subsequentResourcesRequested.Where(url => url.Contains(".wasm")),
|
|
requestedDll => Assert.Contains("/dotnet.wasm", requestedDll));
|
|
|
|
// We also update the cache (add new items, remove unnecessary items)
|
|
var cacheEntryUrls3 = GetCacheEntryUrls();
|
|
Assert.Contains(cacheEntryForComponentsDll, cacheEntryUrls3);
|
|
Assert.Contains(cacheEntryForDotNetWasm, cacheEntryUrls3);
|
|
Assert.DoesNotContain(cacheEntryForDotNetWasmWithChangedHash, cacheEntryUrls3);
|
|
}
|
|
|
|
private IReadOnlyCollection<string> GetCacheEntryUrls()
|
|
{
|
|
var js = @"
|
|
(async function(cacheName, completedCallback) {
|
|
const cache = await caches.open(cacheName);
|
|
const keys = await cache.keys();
|
|
const urls = keys.map(r => r.url);
|
|
completedCallback(urls);
|
|
}).apply(null, arguments)";
|
|
var jsExecutor = (IJavaScriptExecutor)Browser;
|
|
var result = (IEnumerable<object>)jsExecutor.ExecuteAsyncScript(js, CacheName);
|
|
return result.Cast<string>().ToList();
|
|
}
|
|
|
|
private void RemoveCacheEntry(string url)
|
|
{
|
|
var js = @"
|
|
(async function(cacheName, urlToRemove, completedCallback) {
|
|
const cache = await caches.open(cacheName);
|
|
await cache.delete(urlToRemove);
|
|
completedCallback();
|
|
}).apply(null, arguments)";
|
|
((IJavaScriptExecutor)Browser).ExecuteAsyncScript(js, CacheName, url);
|
|
}
|
|
|
|
private void AddCacheEntry(string url, string content)
|
|
{
|
|
var js = @"
|
|
(async function(cacheName, urlToAdd, contentToAdd, completedCallback) {
|
|
const cache = await caches.open(cacheName);
|
|
await cache.put(urlToAdd, new Response(contentToAdd));
|
|
completedCallback();
|
|
}).apply(null, arguments)";
|
|
((IJavaScriptExecutor)Browser).ExecuteAsyncScript(js, CacheName, url, content);
|
|
}
|
|
|
|
private IReadOnlyCollection<string> GetAndClearRequestedPaths()
|
|
{
|
|
var requestLog = _serverFixture.Host.Services.GetRequiredService<BootResourceRequestLog>();
|
|
var result = requestLog.RequestPaths.ToList();
|
|
requestLog.Clear();
|
|
return result;
|
|
}
|
|
|
|
private void WaitUntilLoaded()
|
|
{
|
|
new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until(
|
|
driver => driver.FindElement(By.TagName("h1")).Text == "Hello, world!");
|
|
}
|
|
}
|
|
}
|