();
- })
- .Build();
- }
-}
diff --git a/src/Components/WebAssembly/testassets/MonoSanity/Startup.cs b/src/Components/WebAssembly/testassets/MonoSanity/Startup.cs
deleted file mode 100644
index 3cf349e787..0000000000
--- a/src/Components/WebAssembly/testassets/MonoSanity/Startup.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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 Microsoft.AspNetCore.Builder;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace MonoSanity
-{
- public class Startup
- {
- public void ConfigureServices(IServiceCollection services)
- {
- }
-
- public void Configure(IApplicationBuilder app)
- {
- app.UseDeveloperExceptionPage();
- app.UseFileServer(new FileServerOptions() { EnableDefaultFiles = true, });
- app.UseBlazorFrameworkFiles();
- app.UseStaticFiles();
- app.UseRouting();
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapFallbackToFile("index.html");
- });
- }
- }
-}
diff --git a/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/index.html b/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/index.html
deleted file mode 100644
index 8a42e8e5d1..0000000000
--- a/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/index.html
+++ /dev/null
@@ -1,158 +0,0 @@
-
-
-
- Mono sanity check
-
-
-
- Simple sanity check to ensure the Mono runtime works in basic cases.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Loading...
-
-
-
-
-
diff --git a/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/loader.js b/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/loader.js
deleted file mode 100644
index ad004cee37..0000000000
--- a/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/loader.js
+++ /dev/null
@@ -1,128 +0,0 @@
-(function () {
- // Implement just enough of the DotNet.* API surface for unmarshalled interop calls to work
- // in the cases used in this project
- window.DotNet = {
- jsCallDispatcher: {
- findJSFunction: function (identifier) {
- return window[identifier];
- }
- }
- };
-
- window.initMono = function initMono(loadAssemblyUrls, onReadyCallback) {
- window.Module = {
- locateFile: function (fileName) {
- return fileName === 'dotnet.wasm' ? '/_framework/wasm/dotnet.wasm' : fileName;
- },
- onRuntimeInitialized: function () {
- var allAssemblyUrls = loadAssemblyUrls.concat([
- 'netstandard.dll',
- 'mscorlib.dll',
- 'System.dll',
- 'System.Core.dll',
- 'System.Net.Http.dll',
- 'System.Net.Http.WebAssemblyHttpHandler.dll',
- 'WebAssembly.Bindings.dll'
- ]);
-
- // For these tests we're using Mono's built-in mono_load_runtime_and_bcl util.
- // In real apps we don't use this because we want to have more fine-grained
- // control over how the requests are issued, what gets logged, etc., so for
- // real apps Blazor's Boot.WebAssembly.ts implements its own equivalent.
- MONO.mono_load_runtime_and_bcl(
- /* vfx_prefix */ 'myapp', // Virtual filesystem root - arbitrary value
- /* deploy_prefix */ '_framework/_bin',
- /* enable_debugging */ 1,
- allAssemblyUrls,
- onReadyCallback
- );
- }
- };
-
- addScriptTagsToDocument();
- };
-
- window.invokeMonoMethod = function invokeMonoMethod(assemblyName, namespace, typeName, methodName, args) {
- var assembly_load = Module.cwrap('mono_wasm_assembly_load', 'number', ['string']);
- var find_class = Module.cwrap('mono_wasm_assembly_find_class', 'number', ['number', 'string', 'string']);
- var find_method = Module.cwrap('mono_wasm_assembly_find_method', 'number', ['number', 'string', 'number']);
-
- var assembly = assembly_load(assemblyName);
- var type = find_class(assembly, namespace, typeName);
- var method = find_method(type, methodName, -1);
-
- var stack = Module.stackSave();
- try {
- var resultPtr = callMethod(method, null, args);
- return dotnetStringToJavaScriptString(resultPtr);
- }
- finally {
- Module.stackRestore(stack);
- }
- };
-
- window.dotnetStringToJavaScriptString = function dotnetStringToJavaScriptString(mono_obj) {
- if (mono_obj === 0)
- return null;
- var mono_string_get_utf8 = Module.cwrap('mono_wasm_string_get_utf8', 'number', ['number']);
- var raw = mono_string_get_utf8(mono_obj);
- var res = Module.UTF8ToString(raw);
- Module._free(raw);
- return res;
- };
-
- window.javaScriptStringToDotNetString = function dotnetStringToJavaScriptString(javaScriptString) {
- var mono_string = Module.cwrap('mono_wasm_string_from_js', 'number', ['string']);
- return mono_string(javaScriptString);
- };
-
- function callMethod(method, target, args) {
- var stack = Module.stackSave();
- var invoke_method = Module.cwrap('mono_wasm_invoke_method', 'number', ['number', 'number', 'number']);
-
- try {
- var argsBuffer = Module.stackAlloc(args.length);
- var exceptionFlagManagedInt = Module.stackAlloc(4);
- for (var i = 0; i < args.length; ++i) {
- var argVal = args[i];
- if (typeof argVal === 'number') {
- var managedInt = Module.stackAlloc(4);
- Module.setValue(managedInt, argVal, 'i32');
- Module.setValue(argsBuffer + i * 4, managedInt, 'i32');
- } else if (typeof argVal === 'string') {
- var managedString = javaScriptStringToDotNetString(argVal);
- Module.setValue(argsBuffer + i * 4, managedString, 'i32');
- } else {
- throw new Error('Unsupported arg type: ' + typeof argVal);
- }
- }
- Module.setValue(exceptionFlagManagedInt, 0, 'i32');
-
- var res = invoke_method(method, target, argsBuffer, exceptionFlagManagedInt);
-
- if (Module.getValue(exceptionFlagManagedInt, 'i32') !== 0) {
- throw new Error(dotnetStringToJavaScriptString(res));
- }
-
- return res;
- } finally {
- Module.stackRestore(stack);
- }
- }
-
- async function addScriptTagsToDocument() {
- var browserSupportsNativeWebAssembly = typeof WebAssembly !== 'undefined' && WebAssembly.validate;
- if (!browserSupportsNativeWebAssembly) {
- throw new Error('This browser does not support WebAssembly.');
- }
-
- var bootJson = await fetch('/_framework/blazor.boot.json').then(res => res.json());
- var dotNetJsResourceName = Object.keys(bootJson.resources.runtime)
- .filter(name => name.endsWith('.js'));
-
- var scriptElem = document.createElement('script');
- scriptElem.src = '/_framework/wasm/' + dotNetJsResourceName;
- document.body.appendChild(scriptElem);
- }
-
-})();
diff --git a/src/Components/WebAssembly/testassets/MonoSanityClient/Examples.cs b/src/Components/WebAssembly/testassets/MonoSanityClient/Examples.cs
deleted file mode 100644
index 7bae673449..0000000000
--- a/src/Components/WebAssembly/testassets/MonoSanityClient/Examples.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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 WebAssembly.JSInterop;
-using System;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Net.Http;
-
-namespace MonoSanityClient
-{
- public static class Examples
- {
- public static string AddNumbers(int a, int b)
- => (a + b).ToString();
-
- public static string RepeatString(string str, int count)
- {
- var result = new StringBuilder();
-
- for (var i = 0; i < count; i++)
- {
- result.Append(str);
- }
-
- return result.ToString();
- }
-
- public static void TriggerException(string message)
- {
- throw new InvalidOperationException(message);
- }
-
- public static string EvaluateJavaScript(string expression)
- {
- var result = InternalCalls.InvokeJSUnmarshalled(out var exceptionMessage, "evaluateJsExpression", expression, null, null);
- if (exceptionMessage != null)
- {
- return $".NET got exception: {exceptionMessage}";
- }
-
- return $".NET received: {(result ?? "(NULL)")}";
- }
-
- public static string CallJsNoBoxing(int numberA, int numberB)
- {
- // For tests that call this method, we'll exercise the 'BlazorInvokeJS' code path
- // since that doesn't box the params
- var result = InternalCalls.InvokeJSUnmarshalled(out var exceptionMessage, "divideNumbersUnmarshalled", numberA, numberB, null);
- if (exceptionMessage != null)
- {
- return $".NET got exception: {exceptionMessage}";
- }
-
- return $".NET received: {result}";
- }
-
- public static string GetRuntimeInformation()
- => $"OSDescription: '{RuntimeInformation.OSDescription}';"
- + $" OSArchitecture: '{RuntimeInformation.OSArchitecture}';"
- + $" IsOSPlatform(BROWSER): '{RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))}'";
- }
-}
diff --git a/src/Components/WebAssembly/testassets/MonoSanityClient/InternalCalls.cs b/src/Components/WebAssembly/testassets/MonoSanityClient/InternalCalls.cs
deleted file mode 100644
index 0fb15337e8..0000000000
--- a/src/Components/WebAssembly/testassets/MonoSanityClient/InternalCalls.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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.Runtime.CompilerServices;
-
-namespace WebAssembly.JSInterop
-{
- // This file is copied from https://github.com/dotnet/jsinterop/blob/master/src/Mono.WebAssembly.Interop/InternalCalls.cs
- // so that MonoSanityClient can directly use the same underlying interop APIs (because
- // we're trying to observe the behavior of the Mono runtime itself, not JSInterop).
-
- internal class InternalCalls
- {
- // The exact namespace, type, and method names must match the corresponding entries
- // in driver.c in the Mono distribution
-
- // We're passing asyncHandle by ref not because we want it to be writable, but so it gets
- // passed as a pointer (4 bytes). We can pass 4-byte values, but not 8-byte ones.
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern string InvokeJSMarshalled(out string exception, ref long asyncHandle, string functionIdentifier, string argsJson);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern TRes InvokeJSUnmarshalled(out string exception, string functionIdentifier, T0 arg0, T1 arg1, T2 arg2);
- }
-}
diff --git a/src/Components/WebAssembly/testassets/MonoSanityClient/MonoSanityClient.csproj b/src/Components/WebAssembly/testassets/MonoSanityClient/MonoSanityClient.csproj
deleted file mode 100644
index 9df38c64eb..0000000000
--- a/src/Components/WebAssembly/testassets/MonoSanityClient/MonoSanityClient.csproj
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- netstandard2.1
- false
- exe
- 3.0
-
- true
-
- true
-
-
diff --git a/src/Components/WebAssembly/testassets/MonoSanityClient/Program.cs b/src/Components/WebAssembly/testassets/MonoSanityClient/Program.cs
deleted file mode 100644
index 3bba6ffba6..0000000000
--- a/src/Components/WebAssembly/testassets/MonoSanityClient/Program.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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.
-
-namespace MonoSanityClient
-{
- // Note: Not used at runtime. This exists only to give the server app some type to reference.
- // In realistic scenarios you'd have a Program class for real.
-
- public class Program
- {
- static void Main()
- {
- }
- }
-}
diff --git a/src/Components/WebAssembly/testassets/StandaloneApp/StandaloneApp.csproj b/src/Components/WebAssembly/testassets/StandaloneApp/StandaloneApp.csproj
index 8959978daa..9ebf31c365 100644
--- a/src/Components/WebAssembly/testassets/StandaloneApp/StandaloneApp.csproj
+++ b/src/Components/WebAssembly/testassets/StandaloneApp/StandaloneApp.csproj
@@ -1,9 +1,9 @@
- netstandard2.1
- true
- 3.0
+ net5.0
+ browser-wasm
+ true
true
diff --git a/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/Wasm.Authentication.Client.csproj b/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/Wasm.Authentication.Client.csproj
index 6ade900b99..16fddd3ddc 100644
--- a/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/Wasm.Authentication.Client.csproj
+++ b/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/Wasm.Authentication.Client.csproj
@@ -1,9 +1,10 @@
- netstandard2.1
- 3.0
- true
+ $(DefaultNetCoreTargetFramework)
+ browser-wasm
+ true
+
true
diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj
index 57de501f63..54ec8b638d 100644
--- a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj
+++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj
@@ -1,9 +1,9 @@
- netstandard2.1
- 3.0
- true
+ net5.0
+ browser-wasm
+ true
true
diff --git a/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj b/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj
index fb063e00c8..256ea831e1 100644
--- a/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj
+++ b/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj
@@ -40,8 +40,6 @@
-
-
diff --git a/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs b/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs
index f273f5e53f..1dd32e8221 100644
--- a/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs
+++ b/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs
@@ -48,7 +48,6 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
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("/dotnet.timezones.dat")));
Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith(".js")));
Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith(".dll")));
@@ -60,7 +59,6 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
var subsequentResourcesRequested = GetAndClearRequestedPaths();
Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith("/blazor.boot.json")));
Assert.Empty(subsequentResourcesRequested.Where(path => path.EndsWith("/dotnet.wasm")));
- Assert.Empty(subsequentResourcesRequested.Where(path => path.EndsWith("/dotnet.timezones.dat")));
Assert.NotEmpty(subsequentResourcesRequested.Where(path => path.EndsWith(".js")));
Assert.Empty(subsequentResourcesRequested.Where(path => path.EndsWith(".dll")));
}
diff --git a/src/Components/test/E2ETest/Tests/MonoSanityTest.cs b/src/Components/test/E2ETest/Tests/MonoSanityTest.cs
deleted file mode 100644
index 181a1f9974..0000000000
--- a/src/Components/test/E2ETest/Tests/MonoSanityTest.cs
+++ /dev/null
@@ -1,157 +0,0 @@
-// 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 Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
-using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
-using Microsoft.AspNetCore.E2ETesting;
-using OpenQA.Selenium;
-using OpenQA.Selenium.Support.UI;
-using System;
-using System.Threading.Tasks;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Microsoft.AspNetCore.Components.E2ETest.Tests
-{
- public class MonoSanityTest : ServerTestBase
- {
- public MonoSanityTest(
- BrowserFixture browserFixture,
- AspNetSiteServerFixture serverFixture,
- ITestOutputHelper output)
- : base(browserFixture, serverFixture, output)
- {
- serverFixture.BuildWebHostMethod = MonoSanity.Program.BuildWebHost;
- }
-
- protected override void InitializeAsyncCore()
- {
- Navigate("/", noReload: true);
- WaitUntilMonoRunningInBrowser();
- }
-
- private void WaitUntilMonoRunningInBrowser()
- {
- new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until(driver =>
- {
- return ((IJavaScriptExecutor)driver)
- .ExecuteScript("return window.isTestReady;");
- });
- }
-
- [Fact]
- public void HasTitle()
- {
- Assert.Equal("Mono sanity check", Browser.Title);
- }
-
- [Fact]
- public void CanAddNumbers()
- {
- SetValue(Browser, "addNumberA", "1001");
- SetValue(Browser, "addNumberB", "2002");
- Browser.FindElement(By.CssSelector("#addNumbers button")).Click();
-
- Assert.Equal("3003", GetValue(Browser, "addNumbersResult"));
- }
-
- [Fact]
- public void CanRepeatString()
- {
- SetValue(Browser, "repeatStringStr", "Test");
- SetValue(Browser, "repeatStringCount", "5");
- Browser.FindElement(By.CssSelector("#repeatString button")).Click();
-
- Assert.Equal("TestTestTestTestTest", GetValue(Browser, "repeatStringResult"));
- }
-
- [Fact]
- public void CanReceiveDotNetExceptionInJavaScript()
- {
- SetValue(Browser, "triggerExceptionMessage", "Hello from test");
- Browser.FindElement(By.CssSelector("#triggerException button")).Click();
-
- Assert.Contains("Hello from test", GetValue(Browser, "triggerExceptionMessageStackTrace"));
- }
-
- [Fact]
- public void CanCallJavaScriptFromDotNet()
- {
- SetValue(Browser, "callJsEvalExpression", "getUserAgentString()");
- Browser.FindElement(By.CssSelector("#callJs button")).Click();
- var result = GetValue(Browser, "callJsResult");
- Assert.StartsWith(".NET received: Mozilla", result);
- }
-
- [Fact]
- public void CanReceiveJavaScriptExceptionInDotNet()
- {
- SetValue(Browser, "callJsEvalExpression", "triggerJsException()");
- Browser.FindElement(By.CssSelector("#callJs button")).Click();
- var result = GetValue(Browser, "callJsResult");
- Assert.StartsWith(".NET got exception: This is a JavaScript exception.", result);
-
- // Also verify we got a stack trace
- Assert.Contains("at triggerJsException", result);
- }
-
- [Fact]
- public void CanEvaluateJsExpressionThatResultsInNull()
- {
- SetValue(Browser, "callJsEvalExpression", "null");
- Browser.FindElement(By.CssSelector("#callJs button")).Click();
- var result = GetValue(Browser, "callJsResult");
- Assert.Equal(".NET received: (NULL)", result);
- }
-
- [Fact]
- public void CanEvaluateJsExpressionThatResultsInUndefined()
- {
- SetValue(Browser, "callJsEvalExpression", "console.log('Not returning anything')");
- Browser.FindElement(By.CssSelector("#callJs button")).Click();
- var result = GetValue(Browser, "callJsResult");
- Assert.Equal(".NET received: (NULL)", result);
- }
-
- [Fact]
- public void CanCallJsFunctionsWithoutBoxing()
- {
- SetValue(Browser, "callJsNoBoxingNumberA", "108");
- SetValue(Browser, "callJsNoBoxingNumberB", "4");
- Browser.FindElement(By.CssSelector("#callJsNoBoxing button")).Click();
- Assert.Equal(".NET received: 27", GetValue(Browser, "callJsNoBoxingResult"));
- }
-
- [Fact]
- public void CanCallJsFunctionsWithoutBoxingAndReceiveException()
- {
- SetValue(Browser, "callJsNoBoxingNumberA", "1");
- SetValue(Browser, "callJsNoBoxingNumberB", "0");
- Browser.FindElement(By.CssSelector("#callJsNoBoxing button")).Click();
-
- Assert.StartsWith(".NET got exception: Division by zero", GetValue(Browser, "callJsNoBoxingResult"));
- }
-
- [Fact]
- public void ReturnsExpectedRuntimeInformation()
- {
- Browser.FindElement(By.CssSelector("#getRuntimeInformation button")).Click();
- Assert.Equal(
- "OSDescription: 'web'; OSArchitecture: 'X86'; IsOSPlatform(BROWSER): 'True'",
- GetValue(Browser, "getRuntimeInformationResult"));
- }
-
- private static string GetValue(IWebDriver webDriver, string elementId)
- {
- var element = webDriver.FindElement(By.Id(elementId));
- return element.GetAttribute("value");
- }
-
- private static void SetValue(IWebDriver webDriver, string elementId, string value)
- {
- var element = webDriver.FindElement(By.Id(elementId));
- element.Clear();
- element.SendKeys(value);
- }
- }
-}
diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs b/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs
index 8fce8f9513..998144b33c 100644
--- a/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs
+++ b/src/Components/test/E2ETest/Tests/WebAssemblyAuthenticationTests.cs
@@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
[Fact]
public void WasmAuthentication_Loads()
{
- Assert.Equal("Wasm.Authentication.Client", Browser.Title);
+ Browser.Equal("Wasm.Authentication.Client", () => Browser.Title);
}
[Fact]
@@ -408,8 +408,8 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
private void WaitUntilLoaded(bool skipHeader = false)
{
- new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until(
- driver => driver.FindElement(By.TagName("app")).Text != "Loading...");
+ Browser.Exists(By.TagName("app"));
+ Browser.True(() => Browser.FindElement(By.TagName("app")).Text != "Loading...");
if (!skipHeader)
{
diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyGlobalizationTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyGlobalizationTest.cs
index bf5fcb6f62..a18343dd75 100644
--- a/src/Components/test/E2ETest/Tests/WebAssemblyGlobalizationTest.cs
+++ b/src/Components/test/E2ETest/Tests/WebAssemblyGlobalizationTest.cs
@@ -10,11 +10,13 @@ using OpenQA.Selenium;
using Xunit;
using Xunit.Abstractions;
-namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
+namespace Microsoft.AspNetCore.Components.E2ETest.Tests
{
// For now this is limited to server-side execution because we don't have the ability to set the
// culture in client-side Blazor.
- public class WebAssemblyGlobalizationTest : GlobalizationTest>
+ // This type is internal since localization currently does not work.
+ // Make it public onc https://github.com/dotnet/runtime/issues/38124 is resolved.
+ internal class WebAssemblyGlobalizationTest : GlobalizationTest>
{
public WebAssemblyGlobalizationTest(
BrowserFixture browserFixture,
diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs
index 81c99ccf67..184c62e33c 100644
--- a/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs
+++ b/src/Components/test/E2ETest/Tests/WebAssemblyLocalizationTest.cs
@@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
{
}
- [Theory]
+ [Theory(Skip = "https://github.com/dotnet/runtime/issues/38124")]
[InlineData("en-US", "Hello!")]
[InlineData("fr-FR", "Bonjour!")]
public void CanSetCultureAndReadLocalizedResources(string culture, string message)
diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyLoggingTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyLoggingTest.cs
index 5d83568f50..c6440eecd8 100644
--- a/src/Components/test/E2ETest/Tests/WebAssemblyLoggingTest.cs
+++ b/src/Components/test/E2ETest/Tests/WebAssemblyLoggingTest.cs
@@ -52,7 +52,8 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
AssertLogContainsCriticalMessages(
"crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]",
"[Custom logger] Unhandled exception rendering component: Here is the outer exception",
- "System.InvalidTimeZoneException: Here is the outer exception ---> System.ArithmeticException: Here is the inner exception",
+ "System.InvalidTimeZoneException: Here is the outer exception",
+ "System.ArithmeticException: Here is the inner exception",
"at BasicTestApp.ErrorComponent.ThrowInner");
}
diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyStringComparisonTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyStringComparisonTest.cs
index 9a38268403..61eb71c618 100644
--- a/src/Components/test/E2ETest/Tests/WebAssemblyStringComparisonTest.cs
+++ b/src/Components/test/E2ETest/Tests/WebAssemblyStringComparisonTest.cs
@@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
{
}
- [Fact]
+ [Fact(Skip = "https://github.com/dotnet/runtime/issues/38126")]
public void InvariantCultureWorksAsExpected()
{
Navigate(ServerPathBase, noReload: false);
diff --git a/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj b/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj
index 1cba39fcde..3923c0d5c0 100644
--- a/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj
+++ b/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj
@@ -1,10 +1,10 @@
- netstandard2.1
- 3.0
+ $(DefaultNetCoreTargetFramework)
+ true
+ browser-wasm
- true
--pathbase /subdir
diff --git a/src/Components/test/testassets/BasicTestApp/Program.cs b/src/Components/test/testassets/BasicTestApp/Program.cs
index 01d7c29f00..b512810bc1 100644
--- a/src/Components/test/testassets/BasicTestApp/Program.cs
+++ b/src/Components/test/testassets/BasicTestApp/Program.cs
@@ -42,7 +42,7 @@ namespace BasicTestApp
builder.Logging.Services.AddSingleton(s =>
new PrependMessageLoggerProvider(builder.Configuration["Logging:PrependMessage:Message"], s.GetService()));
-
+
var host = builder.Build();
ConfigureCulture(host);
diff --git a/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js b/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js
index a1f7975a90..dbbba1e0c8 100644
--- a/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js
+++ b/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js
@@ -258,4 +258,4 @@ function receiveDotNetObjectByRefAsync(incomingData) {
testDto: testDto
};
});
-}
+}
\ No newline at end of file
diff --git a/src/Components/test/testassets/ComponentsApp.App/ComponentsApp.App.csproj b/src/Components/test/testassets/ComponentsApp.App/ComponentsApp.App.csproj
index 470c119f13..2680ed8709 100644
--- a/src/Components/test/testassets/ComponentsApp.App/ComponentsApp.App.csproj
+++ b/src/Components/test/testassets/ComponentsApp.App/ComponentsApp.App.csproj
@@ -1,8 +1,7 @@
- netstandard2.0
- 3.0
+ $(DefaultNetCoreTargetFramework)
diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj
index f5ae6e70b0..d9d6b2dda8 100644
--- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj
+++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj
@@ -71,10 +71,6 @@
<_Parameter2>$(TargetFramework)
<_Parameter3>
-
- <_Parameter1>Test.RazorSdkDirectoryRoot
- <_Parameter2>$(RazorSdkDirectoryRoot)
-
diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs
index 9050f00b15..ffa5da4de1 100644
--- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs
+++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs
@@ -44,7 +44,7 @@ namespace Templates.Test
public async Task BlazorWasmStandaloneTemplate_Works()
{
var project = await ProjectFactory.GetOrCreateProject("blazorstandalone", Output);
- project.TargetFramework = "netstandard2.1";
+ project.RuntimeIdentifier = "browser-wasm";
var createResult = await project.RunDotNetNewAsync("blazorwasm");
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
@@ -135,7 +135,7 @@ namespace Templates.Test
public async Task BlazorWasmStandalonePwaTemplate_Works()
{
var project = await ProjectFactory.GetOrCreateProject("blazorstandalonepwa", Output);
- project.TargetFramework = "netstandard2.1";
+ project.RuntimeIdentifier = "browser-wasm";
var createResult = await project.RunDotNetNewAsync("blazorwasm", args: new[] { "--pwa" });
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
@@ -457,8 +457,6 @@ namespace Templates.Test
ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", project, aspNetProcess.Process));
await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html");
- // We only do brotli precompression for published apps
- await AssertCompressionFormat(aspNetProcess, "gzip");
if (BrowserFixture.IsHostAutomationSupported())
{
aspNetProcess.VisitInBrowser(Browser);
@@ -597,7 +595,6 @@ namespace Templates.Test
File.WriteAllText(Path.Combine(serverProject.TemplatePublishDir, "appsettings.json"), testAppSettings);
}
-
private (ProcessEx, string url) RunPublishedStandaloneBlazorProject(Project project)
{
var publishDir = Path.Combine(project.TemplatePublishDir, "wwwroot");
diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/TemplateTests.props.in b/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/TemplateTests.props.in
index 0b1eeedf1b..1ffcbf83d1 100644
--- a/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/TemplateTests.props.in
+++ b/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/TemplateTests.props.in
@@ -34,5 +34,8 @@
several versions older than latest. To avoid a cyclical dependency, this package reference is added to override the bundled version.
Since this is a project reference, we must explicitly import the props file and also specify the output location of the SDK directory.
-->
+
+ ${ArtifactsBinDir}Microsoft.NET.Sdk.Razor\${Configuration}\sdk-output\
+
diff --git a/src/ProjectTemplates/Shared/Project.cs b/src/ProjectTemplates/Shared/Project.cs
index 505716e01e..939a49089f 100644
--- a/src/ProjectTemplates/Shared/Project.cs
+++ b/src/ProjectTemplates/Shared/Project.cs
@@ -39,10 +39,10 @@ namespace Templates.Test.Helpers
public string ProjectGuid { get; set; }
public string TemplateOutputDir { get; set; }
public string TargetFramework { get; set; } = GetAssemblyMetadata("Test.DefaultTargetFramework");
- public string RazorSdkDirectoryRoot { get; set; } = GetAssemblyMetadata("Test.RazorSdkDirectoryRoot");
+ public string RuntimeIdentifier { get; set; } = string.Empty;
- public string TemplateBuildDir => Path.Combine(TemplateOutputDir, "bin", "Debug", TargetFramework);
- public string TemplatePublishDir => Path.Combine(TemplateOutputDir, "bin", "Release", TargetFramework, "publish");
+ public string TemplateBuildDir => Path.Combine(TemplateOutputDir, "bin", "Debug", TargetFramework, RuntimeIdentifier);
+ public string TemplatePublishDir => Path.Combine(TemplateOutputDir, "bin", "Release", TargetFramework, RuntimeIdentifier, "publish");
public ITestOutputHelper Output { get; set; }
public IMessageSink DiagnosticsMessageSink { get; set; }
@@ -117,9 +117,7 @@ namespace Templates.Test.Helpers
// Avoid restoring as part of build or publish. These projects should have already restored as part of running dotnet new. Explicitly disabling restore
// should avoid any global contention and we can execute a build or publish in a lock-free way
- var razorSDKarg = string.IsNullOrEmpty(RazorSdkDirectoryRoot) ? string.Empty : $"/p:RazorSdkDirectoryRoot={RazorSdkDirectoryRoot}";
-
- using var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"publish --no-restore -c Release /bl {razorSDKarg} {additionalArgs}", packageOptions);
+ using var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"publish --no-restore -c Release /bl {additionalArgs}", packageOptions);
await result.Exited;
CaptureBinLogOnFailure(result);
return new ProcessResult(result);
@@ -132,9 +130,7 @@ namespace Templates.Test.Helpers
// Avoid restoring as part of build or publish. These projects should have already restored as part of running dotnet new. Explicitly disabling restore
// should avoid any global contention and we can execute a build or publish in a lock-free way
- var razorSDKarg = string.IsNullOrEmpty(RazorSdkDirectoryRoot) ? string.Empty : $"/p:RazorSdkDirectoryRoot={RazorSdkDirectoryRoot}";
-
- using var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"build --no-restore -c Debug /bl {razorSDKarg} {additionalArgs}", packageOptions);
+ using var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"build --no-restore -c Debug /bl {additionalArgs}", packageOptions);
await result.Exited;
CaptureBinLogOnFailure(result);
return new ProcessResult(result);
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Client.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Client.csproj.in
index 4b856535b2..24c38f6480 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Client.csproj.in
+++ b/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Client.csproj.in
@@ -1,8 +1,9 @@
- netstandard2.1
- 3.0
+ ${DefaultNetCoreTargetFramework}
+ browser-wasm
+ true
service-worker-assets.js
@@ -13,7 +14,6 @@
-
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Shared.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Shared.csproj.in
index d4c395e8cb..797f21c216 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Shared.csproj.in
+++ b/src/ProjectTemplates/Web.ProjectTemplates/ComponentsWebAssembly-CSharp.Shared.csproj.in
@@ -1,7 +1,7 @@
- netstandard2.1
+ ${DefaultNetCoreTargetFramework}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj
index d36a8d5050..e142e3233a 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj
+++ b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj
@@ -35,7 +35,6 @@
-
diff --git a/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in b/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in
index 227bffd7a5..c3e1a9634d 100644
--- a/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in
+++ b/src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in
@@ -34,5 +34,8 @@
We reference the project to ensure it's built before the other projects that use it. Since this is a project reference, we
must explicitly import the props file and also specify the output location of the SDK directory.
-->
+
+ ${ArtifactsBinDir}Microsoft.NET.Sdk.Razor\${Configuration}\sdk-output\
+
diff --git a/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj b/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj
index 5bc7af6a32..69e08a8c83 100644
--- a/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj
+++ b/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj
@@ -1,4 +1,4 @@
-
+
@@ -65,10 +65,6 @@
<_Parameter1>Test.DefaultTargetFramework
<_Parameter2>$(DefaultNetCoreTargetFramework)
-
- <_Parameter1>Test.RazorSdkDirectoryRoot
- <_Parameter2>$(RazorSdkDirectoryRoot)
-
<_Parameter1>ContinuousIntegrationBuild
<_Parameter2>true
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/Application.cs b/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/Application.cs
index 9c63203f66..73a05d570f 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/Application.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/Application.cs
@@ -39,6 +39,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
Commands.Add(new ShutdownCommand(this));
Commands.Add(new DiscoverCommand(this));
Commands.Add(new GenerateCommand(this));
+ Commands.Add(new BrotliCompressCommand(this));
}
public CancellationToken CancellationToken { get; }
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/BrotliCompressCommand.cs b/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/BrotliCompressCommand.cs
new file mode 100644
index 0000000000..ca758cd6d3
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/BrotliCompressCommand.cs
@@ -0,0 +1,70 @@
+// 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.IO;
+using System.IO.Compression;
+using System.Threading.Tasks;
+using Microsoft.Extensions.CommandLineUtils;
+
+namespace Microsoft.AspNetCore.Razor.Tools
+{
+ internal class BrotliCompressCommand : CommandBase
+ {
+ public BrotliCompressCommand(Application parent)
+ : base(parent, "brotli")
+ {
+ Sources = Option("-s", "files to compress", CommandOptionType.MultipleValue);
+ Outputs = Option("-o", "Output file path", CommandOptionType.MultipleValue);
+ CompressionLevelOption = Option("-c", "Compression level", CommandOptionType.SingleValue);
+ }
+
+ public CommandOption Sources { get; }
+
+ public CommandOption Outputs { get; }
+
+ public CommandOption CompressionLevelOption { get; }
+
+ public CompressionLevel CompressionLevel { get; private set; } = CompressionLevel.Optimal;
+
+ protected override bool ValidateArguments()
+ {
+ if (Sources.Values.Count != Outputs.Values.Count)
+ {
+ Error.WriteLine($"{Sources.Description} has {Sources.Values.Count}, but {Outputs.Description} has {Outputs.Values.Count} values.");
+ return false;
+ }
+
+ if (CompressionLevelOption.HasValue())
+ {
+ if (!Enum.TryParse(CompressionLevelOption.Value(), out var value))
+ {
+ Error.WriteLine($"Invalid option {CompressionLevelOption.Value()} for {CompressionLevelOption.Template}.");
+ return false;
+ }
+
+ CompressionLevel = value;
+ }
+
+ return true;
+ }
+
+ protected override Task ExecuteCoreAsync()
+ {
+ Parallel.For(0, Sources.Values.Count, i =>
+ {
+ var source = Sources.Values[i];
+ var output = Outputs.Values[i];
+
+ using var sourceStream = File.OpenRead(source);
+ using var fileStream = new FileStream(output, FileMode.Create);
+
+ using var stream = new BrotliStream(fileStream, CompressionLevel);
+
+ sourceStream.CopyTo(stream);
+ });
+
+ return Task.FromResult(ExitCodeSuccess);
+ }
+ }
+}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs
index 72e8d79e49..8648ede045 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs
@@ -9,6 +9,7 @@ using System.IO.Compression;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
+using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
@@ -238,6 +239,26 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
}
}
+ public static void FileHashEquals(MSBuildResult result, string filePath, string expectedSha256Base64)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException(nameof(result));
+ }
+
+ filePath = Path.Combine(result.Project.DirectoryPath, filePath);
+ FileExists(result, filePath);
+
+ var actual = File.ReadAllBytes(filePath);
+ using var algorithm = SHA256.Create();
+ var actualSha256 = algorithm.ComputeHash(actual);
+ var actualSha256Base64 = Convert.ToBase64String(actualSha256);
+ if (expectedSha256Base64 != actualSha256Base64)
+ {
+ throw new MSBuildXunitException(result, $"File hashes for {filePath} do not match. Expected {expectedSha256Base64}, Actual {actualSha256Base64}");
+ }
+ }
+
public static void FileContainsLine(MSBuildResult result, string filePath, string match)
{
if (result == null)
@@ -298,7 +319,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
return filePath;
}
- public static void FileCountEquals(MSBuildResult result, int expected, string directoryPath, string searchPattern)
+ public static void FileCountEquals(MSBuildResult result, int expected, string directoryPath, string searchPattern, SearchOption searchOption = SearchOption.AllDirectories)
{
if (result == null)
{
@@ -319,7 +340,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
if (Directory.Exists(directoryPath))
{
- var files = Directory.GetFiles(directoryPath, searchPattern, SearchOption.AllDirectories);
+ var files = Directory.GetFiles(directoryPath, searchPattern, searchOption);
if (files.Length != expected)
{
throw new FileCountException(result, expected, directoryPath, searchPattern, files);
@@ -508,6 +529,44 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
}
}
+ public static void AssemblyContainsResource(MSBuildResult result, string assemblyPath, string resourceName)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException(nameof(result));
+ }
+
+ assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath));
+
+ var resources = GetAssemblyResourceNames(assemblyPath);
+ Assert.Contains(resourceName, resources);
+ }
+
+ public static void AssemblyDoesNotContainResource(MSBuildResult result, string assemblyPath, string resourceName)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException(nameof(result));
+ }
+
+ assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath));
+
+ var resources = GetAssemblyResourceNames(assemblyPath);
+ Assert.DoesNotContain(resourceName, resources);
+ }
+
+ private static IEnumerable GetAssemblyResourceNames(string assemblyPath)
+ {
+ using var file = File.OpenRead(assemblyPath);
+ using var peReader = new PEReader(file);
+ var metadataReader = peReader.GetMetadataReader();
+ return metadataReader.ManifestResources.Where(r => !r.IsNil).Select(r =>
+ {
+ var resource = metadataReader.GetManifestResource(r);
+ return metadataReader.GetString(resource.Name);
+ }).ToArray();
+ }
+
public static void AssemblyHasAttribute(MSBuildResult result, string assemblyPath, string fullTypeName)
{
if (result == null)
@@ -538,14 +597,20 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
}
}
- private abstract class MSBuildXunitException : Xunit.Sdk.XunitException
+ public class MSBuildXunitException : Xunit.Sdk.XunitException
{
protected MSBuildXunitException(MSBuildResult result)
{
Result = result;
}
- protected abstract string Heading { get; }
+ public MSBuildXunitException(MSBuildResult result, string heading)
+ {
+ Result = result;
+ Heading = heading;
+ }
+
+ protected virtual string Heading { get; }
public MSBuildResult Result { get; }
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs
index 5a64947f8b..4d1b03af59 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs
@@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
[InitializeTestProject("SimpleMvc")]
public async Task Build_RazorOutputPath_SetToNonDefault()
{
- var customOutputPath = Path.Combine("bin", Configuration, TargetFramework, "Razor");
+ var customOutputPath = Path.Combine("bin", Configuration, Project.TargetFramework, "Razor");
var result = await DotnetMSBuild("Build", $"/p:RazorOutputPath={customOutputPath}");
Assert.BuildPassed(result);
@@ -135,7 +135,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
[InitializeTestProject("SimpleMvc")]
public async Task Build_MvcRazorOutputPath_SetToNonDefault()
{
- var customOutputPath = Path.Combine("bin", Configuration, TargetFramework, "Razor");
+ var customOutputPath = Path.Combine("bin", Configuration, Project.TargetFramework, "Razor");
var result = await DotnetMSBuild("Build", $"/p:MvcRazorOutputPath={customOutputPath}");
Assert.BuildPassed(result);
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildServerIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildServerIntegrationTest.cs
index 1fe81b585a..23faba7d40 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildServerIntegrationTest.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildServerIntegrationTest.cs
@@ -12,12 +12,10 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{
public class BuildServerIntegrationTest : MSBuildIntegrationTestBase, IClassFixture
{
- private BuildServerTestFixture _buildServer;
public BuildServerIntegrationTest(BuildServerTestFixture buildServer)
: base(buildServer)
{
- _buildServer = buildServer;
}
[Fact]
@@ -171,7 +169,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
var toolAssembly = Path.Combine(publishDir, "rzc.dll");
var result = await DotnetMSBuild(
"Build",
- $"/p:_RazorForceBuildServer=true /p:_RazorToolAssembly={toolAssembly}",
+ $"/p:_RazorForceBuildServer=true /p:_RazorSdkToolAssembly={toolAssembly}",
suppressBuildServer: true); // We don't want to specify a pipe name
Assert.BuildPassed(result);
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs
index 544e98e281..797d244625 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs
@@ -20,5 +20,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
public static string RazorSdkDirectoryRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "RazorSdkDirectoryRoot").Value;
public static string RepoRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "Testing.RepoRoot").Value;
+
+ public static string DefaultNetCoreTargetFramework => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "DefaultNetCoreTargetFramework").Value;
}
}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponents31IntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponents31IntegrationTest.cs
index a3422fe9ea..6c09b10104 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponents31IntegrationTest.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponents31IntegrationTest.cs
@@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
[InitializeTestProject("blazor31")]
public async Task Build_Components_WithDotNetCoreMSBuild_Works()
{
- TargetFramework = "netcoreapp3.1";
+ Project.TargetFramework = "netcoreapp3.1";
var result = await DotnetMSBuild("Build");
Assert.BuildPassed(result);
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponentsIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponentsIntegrationTest.cs
index ce867b6b8d..fa6e78c7d7 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponentsIntegrationTest.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildWithComponentsIntegrationTest.cs
@@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
[InitializeTestProject("ComponentLibrary")]
public async Task Build_WithoutRazorLangVersion_ProducesWarning()
{
- TargetFramework = "netstandard2.0";
+ Project.TargetFramework = "netstandard2.0";
var result = await DotnetMSBuild("Build", "/p:RazorLangVersion=");
Assert.BuildPassed(result, allowWarnings: true);
@@ -80,7 +80,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
[InitializeTestProject("ComponentLibrary")]
public async Task Building_NetstandardComponentLibrary()
{
- TargetFramework = "netstandard2.0";
+ Project.TargetFramework = "netstandard2.0";
// Build
var result = await DotnetMSBuild("Build");
@@ -96,7 +96,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
[InitializeTestProject("ComponentLibrary")]
public async Task Build_DoesNotProduceRefsDirectory()
{
- TargetFramework = "netstandard2.0";
+ Project.TargetFramework = "netstandard2.0";
// Build
var result = await DotnetMSBuild("Build");
@@ -111,7 +111,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
[InitializeTestProject("ComponentLibrary")]
public async Task Publish_DoesNotProduceRefsDirectory()
{
- TargetFramework = "netstandard2.0";
+ Project.TargetFramework = "netstandard2.0";
// Build
var result = await DotnetMSBuild("Publish");
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/DesignTimeBuildIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/DesignTimeBuildIntegrationTest.cs
index f5baa8b4b9..8576598145 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/DesignTimeBuildIntegrationTest.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/DesignTimeBuildIntegrationTest.cs
@@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
[InitializeTestProject("ComponentLibrary")]
public async Task RazorGenerateComponentDesignTime_ReturnsRazorComponentWithTargetPath()
{
- TargetFramework = "netstandard2.0";
+ Project.TargetFramework = "netstandard2.0";
var result = await DotnetMSBuild("RazorGenerateComponentDesignTime;_IntrospectRazorComponentWithTargetPath");
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs
index 0266ecdd1b..81faa7d2a1 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs
@@ -2,7 +2,9 @@
// 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.IO;
+using System.Linq;
using System.Security.Cryptography;
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
@@ -36,6 +38,24 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
return new FileThumbPrint(path, lastWriteTimeUtc, hash);
}
+ ///
+ /// Returns a list of thumbprints for all files (recursive) in the specified directory, sorted by file paths.
+ ///
+ internal static List CreateFolderThumbprint(ProjectDirectory project, string directoryPath, params string[] filesToIgnore)
+ {
+ directoryPath = System.IO.Path.Combine(project.DirectoryPath, directoryPath);
+ var files = Directory.GetFiles(directoryPath).Where(p => !filesToIgnore.Contains(p));
+ var thumbprintLookup = new List();
+ foreach (var file in files)
+ {
+ var thumbprint = Create(file);
+ thumbprintLookup.Add(thumbprint);
+ }
+
+ thumbprintLookup.Sort(Comparer.Create((a, b) => StringComparer.Ordinal.Compare(a.Path, b.Path)));
+ return thumbprintLookup;
+ }
+
public bool Equals(FileThumbPrint other)
{
return
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/InitializeTestProjectAttribute.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/InitializeTestProjectAttribute.cs
index ccc1b06af9..8293d4488a 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/InitializeTestProjectAttribute.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/InitializeTestProjectAttribute.cs
@@ -36,12 +36,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
throw new InvalidOperationException($"This should be used on a class derived from {typeof(MSBuildIntegrationTestBase)}");
}
- MSBuildIntegrationTestBase.Project = ProjectDirectory.Create(_originalProjectName, _testProjectName, _baseDirectory, _additionalProjects, _language);
-#if NETCOREAPP
- MSBuildIntegrationTestBase.TargetFramework = "net5.0";
-#else
-#error Target frameworks need to be updated
-#endif
+ MSBuildIntegrationTestBase.Project = ProjectDirectory.Create(_originalProjectName, new ProjectDirectory.ProjectDirectoryOptions(_baseDirectory, _testProjectName, _language), _additionalProjects);
}
public override void After(MethodInfo methodUnderTest)
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildIntegrationTestBase.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildIntegrationTestBase.cs
index ff17163390..cc9fd1009a 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildIntegrationTestBase.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildIntegrationTestBase.cs
@@ -16,7 +16,6 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
public abstract class MSBuildIntegrationTestBase
{
private static readonly AsyncLocal _project = new AsyncLocal();
- private static readonly AsyncLocal _projectTfm = new AsyncLocal();
protected MSBuildIntegrationTestBase(BuildServerTestFixtureBase buildServer)
{
@@ -31,9 +30,9 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
#error Configuration not supported
#endif
- protected string IntermediateOutputPath => Path.Combine("obj", Configuration, TargetFramework);
+ protected string IntermediateOutputPath => Path.Combine("obj", Configuration, Project.TargetFramework);
- protected string OutputPath => Path.Combine("bin", Configuration, TargetFramework);
+ protected string OutputPath => Path.Combine("bin", Configuration, Project.TargetFramework);
protected string PublishOutputPath => Path.Combine(OutputPath, "publish");
@@ -50,12 +49,6 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
protected string RazorComponentIntermediateOutputPath => Path.Combine(IntermediateOutputPath, "RazorDeclaration");
- internal static string TargetFramework
- {
- get => _projectTfm.Value;
- set => _projectTfm.Value = value;
- }
-
protected BuildServerTestFixtureBase BuildServer { get; set; }
internal Task DotnetMSBuild(
@@ -111,16 +104,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
}
internal void AddProjectFileContent(string content)
- {
- if (content == null)
- {
- throw new ArgumentNullException(nameof(content));
- }
-
- var existing = File.ReadAllText(Project.ProjectFilePath);
- var updated = existing.Replace("", content);
- File.WriteAllText(Project.ProjectFilePath, updated);
- }
+ => Project.AddProjectFileContent(content);
internal void ReplaceContent(string content, params string[] paths)
{
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs
index e9a9216782..5ab553d6bb 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs
@@ -2,6 +2,7 @@
// 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.Diagnostics;
using System.IO;
using System.Linq;
@@ -15,6 +16,47 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{
internal static class MSBuildProcessManager
{
+ internal static Task DotnetMSBuild(
+ ProjectDirectory project,
+ string target = "Build",
+ string args = null,
+ string buildServerPipeName = null)
+ {
+ var buildArgumentList = new List
+ {
+ // Disable node-reuse. We don't want msbuild processes to stick around
+ // once the test is completed.
+ "/nr:false",
+
+ // Always generate a bin log for debugging purposes
+ "/bl",
+
+ // Let the test app know it is running as part of a test.
+ "/p:RunningAsTest=true",
+
+ $"/p:MicrosoftNETCoreAppRuntimeVersion={BuildVariables.MicrosoftNETCoreAppRuntimeVersion}",
+ $"/p:MicrosoftNetCompilersToolsetPackageVersion={BuildVariables.MicrosoftNetCompilersToolsetPackageVersion}",
+ $"/p:RazorSdkDirectoryRoot={BuildVariables.RazorSdkDirectoryRoot}",
+ $"/p:RepoRoot={BuildVariables.RepoRoot}",
+ $"/p:Configuration={project.Configuration}",
+ $"/t:{target}",
+ args,
+ };
+
+ if (buildServerPipeName != null)
+ {
+ buildArgumentList.Add($@"/p:_RazorBuildServerPipeName=""{buildServerPipeName}""");
+ }
+
+ var buildArguments = string.Join(" ", buildArgumentList);
+
+ return RunProcessAsync(
+ project,
+ buildArguments,
+ timeout: null,
+ MSBuildProcessKind.Dotnet);
+ }
+
public static async Task RunProcessAsync(
ProjectDirectory project,
string arguments,
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj
index a28e8d3e91..a1e160322e 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj
@@ -50,6 +50,11 @@
<_Parameter2>$(MicrosoftNETCoreAppRuntimeVersion)
+
+ <_Parameter1>DefaultNetCoreTargetFramework
+ <_Parameter2>$(DefaultNetCoreTargetFramework)
+
+
<_Parameter1>MicrosoftNetCompilersToolsetPackageVersion
<_Parameter2>$(MicrosoftNetCompilersToolsetPackageVersion)
@@ -72,6 +77,11 @@
+
+
+
+
+
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MvcBuildIntegrationTestLegacy.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MvcBuildIntegrationTestLegacy.cs
index dbc85428aa..f73bdeb2d6 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MvcBuildIntegrationTestLegacy.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MvcBuildIntegrationTestLegacy.cs
@@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
IClassFixture
{
public abstract string TestProjectName { get; }
- public abstract new string TargetFramework { get; }
+ public abstract string TargetFramework { get; }
public virtual string OutputFileName => $"{TestProjectName}.dll";
public MvcBuildIntegrationTestLegacy(LegacyBuildServerTestFixture buildServer)
@@ -24,8 +24,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
protected IDisposable CreateTestProject()
{
- Project = ProjectDirectory.Create(TestProjectName, TestProjectName, string.Empty, Array.Empty(), "C#");
- MSBuildIntegrationTestBase.TargetFramework = TargetFramework;
+ Project = ProjectDirectory.Create(TestProjectName);
+ Project.TargetFramework = TargetFramework;
return new Disposable();
}
@@ -52,7 +52,6 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
result,
Path.Combine(IntermediateOutputPath, $"{TestProjectName}.TagHelpers.output.cache"),
@"""Name"":""SimpleMvc.SimpleTagHelper""");
-
}
}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/PackIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/PackIntegrationTest.cs
index dfe9c90f48..6b0688031c 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/PackIntegrationTest.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/PackIntegrationTest.cs
@@ -258,7 +258,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
[InitializeTestProject("ComponentLibrary")]
public async Task Pack_DoesNotIncludeAnyCustomPropsFiles_WhenNoStaticAssetsAreAvailable()
{
- MSBuildIntegrationTestBase.TargetFramework = "netstandard2.0";
+ Project.TargetFramework = "netstandard2.0";
var result = await DotnetMSBuild("Pack");
@@ -282,7 +282,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
[InitializeTestProject("PackageLibraryTransitiveDependency")]
public async Task Pack_Incremental_DoesNotRegenerateCacheAndPropsFiles()
{
- TargetFramework = "netstandard2.0";
+ Project.TargetFramework = "netstandard2.0";
var result = await DotnetMSBuild("Pack");
Assert.BuildPassed(result, allowWarnings: true);
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs
index e2a28a1b06..d0d0e571f4 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
-using Microsoft.AspNetCore.Testing;
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{
@@ -18,9 +17,28 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
public bool PreserveWorkingDirectory { get; set; }
#endif
- public static ProjectDirectory Create(string originalProjectName, string targetProjectName, string baseDirectory, string[] additionalProjects, string language)
+ public readonly struct ProjectDirectoryOptions
{
- var destinationPath = Path.Combine(Path.GetTempPath(), "Razor", baseDirectory, Path.GetRandomFileName());
+ public ProjectDirectoryOptions(string baseDirectory, string targetProjectName, string language)
+ {
+ BaseDirectory = baseDirectory;
+ TargetProjectName = targetProjectName;
+ Language = language;
+ }
+
+ public string TargetProjectName { get; }
+
+ public string BaseDirectory { get; }
+
+ public string Language { get; }
+ }
+
+ public static ProjectDirectory Create(string projectName, params string[] additionalProjects) => Create(projectName, default, additionalProjects);
+
+ public static ProjectDirectory Create(string originalProjectName, ProjectDirectoryOptions options, params string[] additionalProjects)
+ {
+ // string targetProjectName, string baseDirectory,
+ var destinationPath = Path.Combine(Path.GetTempPath(), "Razor", options.BaseDirectory ?? string.Empty, Path.GetRandomFileName());
Directory.CreateDirectory(destinationPath);
try
@@ -50,30 +68,27 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
}
// Rename the csproj/fsproj
- string extension;
- if (language.Equals("C#", StringComparison.OrdinalIgnoreCase))
- {
- extension = ".csproj";
- }
- else if (language.Equals("F#", StringComparison.OrdinalIgnoreCase))
+ var extension = ".csproj";
+ if (string.Equals("F#", options.Language, StringComparison.OrdinalIgnoreCase))
{
extension = ".fsproj";
}
- else
- {
- throw new InvalidOperationException($"Language {language} is not supported.");
- }
+
var directoryPath = Path.Combine(destinationPath, originalProjectName);
- var oldProjectFilePath = Path.Combine(directoryPath, originalProjectName + extension);
- var newProjectFilePath = Path.Combine(directoryPath, targetProjectName + extension);
- File.Move(oldProjectFilePath, newProjectFilePath);
+ var projectFilePath = Path.Combine(directoryPath, originalProjectName + extension);
+ if (options.TargetProjectName != null)
+ {
+ var newProjectFilePath = Path.Combine(directoryPath, options.TargetProjectName + extension);
+ File.Move(projectFilePath, newProjectFilePath);
+ projectFilePath = newProjectFilePath;
+ }
CopyRepositoryAssets(repositoryRoot, destinationPath);
return new ProjectDirectory(
destinationPath,
directoryPath,
- newProjectFilePath);
+ projectFilePath);
}
catch
{
@@ -127,7 +142,7 @@ $@"
";
File.WriteAllText(Path.Combine(projectDestination, "Before.Directory.Build.props"), beforeDirectoryPropsContent);
- new List { "Directory.Build.props", "Directory.Build.targets", "RazorTest.Introspection.targets" }
+ new List { "Directory.Build.props", "Directory.Build.targets", "RazorTest.Introspection.targets", "blazor.webassembly.js" }
.ForEach(file =>
{
var source = Path.Combine(testAppsRoot, file);
@@ -149,6 +164,12 @@ $@"
}
}
+ public ProjectDirectory GetSibling(string projectName)
+ {
+ var siblingDirectory = Path.GetFullPath(Path.Combine(DirectoryPath, "..", projectName));
+ return new ProjectDirectory(SolutionPath, siblingDirectory, Path.Combine(siblingDirectory, projectName + ".csproj"));
+ }
+
protected ProjectDirectory(string solutionPath, string directoryPath, string projectFilePath)
{
SolutionPath = solutionPath;
@@ -156,12 +177,38 @@ $@"
ProjectFilePath = projectFilePath;
}
+ public string TargetFramework { get; set; } = BuildVariables.DefaultNetCoreTargetFramework;
+
+ public string RuntimeIdentifier { get; set; } = string.Empty;
+
+ public string Configuration { get; set; } =
+#if DEBUG
+ "Debug";
+#else
+ "Release";
+#endif
+
+ ///
+ /// Razor-Temp\unique-id\project
+ ///
public string DirectoryPath { get; }
+ ///
+ /// Razor-Temp\unique-id\project\project.csproj
+ ///
public string ProjectFilePath { get;}
+ ///
+ /// Razor-Temp\unique-id\
+ ///
public string SolutionPath { get; }
+ public string IntermediateOutputDirectory => Path.Combine("obj", Configuration, TargetFramework, RuntimeIdentifier);
+
+ public string BuildOutputDirectory => Path.Combine("bin", Configuration, TargetFramework, RuntimeIdentifier);
+
+ public string PublishOutputDirectory => Path.Combine(BuildOutputDirectory, "publish");
+
public void Dispose()
{
if (PreserveWorkingDirectory)
@@ -210,5 +257,31 @@ $@"
throw new Exception($"File {fileName} could not be found in {baseDirectory} or its parent directories.");
}
+
+ internal void AddProjectFileContent(string content)
+ {
+ if (content == null)
+ {
+ throw new ArgumentNullException(nameof(content));
+ }
+
+ var existing = File.ReadAllText(ProjectFilePath);
+ var updated = existing.Replace("", content);
+ File.WriteAllText(ProjectFilePath, updated);
+ }
+
+ internal void AddDirectoryBuildContent(string content)
+ {
+ if (content == null)
+ {
+ throw new ArgumentNullException(nameof(content));
+ }
+
+ var filepath = Path.Combine(DirectoryPath, "Directory.Build.props");
+
+ var existing = File.ReadAllText(filepath);
+ var updated = existing.Replace("", content);
+ File.WriteAllText(filepath, updated);
+ }
}
}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs
new file mode 100644
index 0000000000..7585b5b76c
--- /dev/null
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs
@@ -0,0 +1,80 @@
+// 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.IO;
+using System.Linq;
+using System.Text.Json;
+using Microsoft.AspNetCore.Razor.Tasks;
+
+namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
+{
+ internal static class ServiceWorkerAssert
+ {
+ internal static void VerifyServiceWorkerFiles(MSBuildResult result, string outputDirectory, string serviceWorkerPath, string serviceWorkerContent, string assetsManifestPath)
+ {
+ // Check the expected files are there
+ var serviceWorkerResolvedPath = Assert.FileExists(result, outputDirectory, serviceWorkerPath);
+ var assetsManifestResolvedPath = Assert.FileExists(result, outputDirectory, assetsManifestPath);
+
+ // Check the service worker contains the expected content (which comes from the PublishedContent file)
+ Assert.FileContains(result, serviceWorkerResolvedPath, serviceWorkerContent);
+
+ // Check the assets manifest version was added to the published service worker
+ var assetsManifest = ReadServiceWorkerAssetsManifest(assetsManifestResolvedPath);
+ Assert.FileContains(result, serviceWorkerResolvedPath, $"/* Manifest version: {assetsManifest.version} */");
+
+ // Check the assets manifest contains correct entries for all static content we're publishing
+ var resolvedPublishDirectory = Path.Combine(result.Project.DirectoryPath, outputDirectory);
+ var outputFiles = Directory.GetFiles(resolvedPublishDirectory, "*", new EnumerationOptions { RecurseSubdirectories = true });
+ var assetsManifestHashesByUrl = (IReadOnlyDictionary)assetsManifest.assets.ToDictionary(x => x.url, x => x.hash);
+ foreach (var filePath in outputFiles)
+ {
+ var relativePath = Path.GetRelativePath(resolvedPublishDirectory, filePath);
+
+ // We don't list compressed files in the SWAM, as these are transparent to the client,
+ // nor do we list the service worker itself or its assets manifest, as these don't need to be fetched in the same way
+ if (IsCompressedFile(relativePath)
+ || string.Equals(relativePath, serviceWorkerPath, StringComparison.Ordinal)
+ || string.Equals(relativePath, assetsManifestPath, StringComparison.Ordinal))
+ {
+ continue;
+ }
+
+ // Verify hash
+ var fileUrl = relativePath.Replace('\\', '/');
+ var expectedHash = ParseWebFormattedHash(assetsManifestHashesByUrl[fileUrl]);
+ Assert.Contains(fileUrl, assetsManifestHashesByUrl);
+ Assert.FileHashEquals(result, filePath, expectedHash);
+ }
+ }
+
+ private static string ParseWebFormattedHash(string webFormattedHash)
+ {
+ Assert.StartsWith("sha256-", webFormattedHash);
+ return webFormattedHash.Substring(7);
+ }
+
+ private static bool IsCompressedFile(string path)
+ {
+ switch (Path.GetExtension(path))
+ {
+ case ".br":
+ case ".gz":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static AssetsManifestFile ReadServiceWorkerAssetsManifest(string assetsManifestResolvedPath)
+ {
+ var jsContents = File.ReadAllText(assetsManifestResolvedPath);
+ var jsonStart = jsContents.IndexOf("{");
+ var jsonLength = jsContents.LastIndexOf("}") - jsonStart + 1;
+ var json = jsContents.Substring(jsonStart, jsonLength);
+ return JsonSerializer.Deserialize(json);
+ }
+ }
+}
diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIncrementalismTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs
similarity index 72%
rename from src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIncrementalismTest.cs
rename to src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs
index 46a85cdea1..301d688e40 100644
--- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIncrementalismTest.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs
@@ -4,17 +4,18 @@
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.Tasks;
using Xunit;
-namespace Microsoft.AspNetCore.Components.WebAssembly.Build
+namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{
- public class BuildIncrementalismTest
+ public class WasmBuildIncrementalismTest
{
[Fact]
public async Task Build_WithLinker_IsIncremental()
{
// Arrange
- using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
var result = await MSBuildProcessManager.DotnetMSBuild(project);
Assert.BuildPassed(result);
@@ -43,14 +44,14 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public async Task Build_SatelliteAssembliesFileIsPreserved()
{
// Arrange
- using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
File.Move(Path.Combine(project.DirectoryPath, "Resources.ja.resx.txt"), Path.Combine(project.DirectoryPath, "Resource.ja.resx"));
var result = await MSBuildProcessManager.DotnetMSBuild(project);
Assert.BuildPassed(result);
- var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor", "blazor.satelliteasm.props");
- var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "ja", "standalone.resources.dll");
+ var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor.satelliteasm.props");
+ var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "ja", "blazorwasm.resources.dll");
var bootJson = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
// Assert
@@ -76,11 +77,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileExists(result, satelliteAssemblyCacheFile);
Assert.FileExists(result, satelliteAssemblyFile);
- var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
var satelliteResources = bootJsonFile.resources.satelliteResources;
var kvp = Assert.Single(satelliteResources);
Assert.Equal("ja", kvp.Key);
- Assert.Equal("ja/standalone.resources.dll", Assert.Single(kvp.Value).Key);
+ Assert.Equal("ja/blazorwasm.resources.dll", Assert.Single(kvp.Value).Key);
}
}
@@ -88,13 +89,13 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public async Task Build_SatelliteAssembliesFileIsCreated_IfNewFileIsAdded()
{
// Arrange
- using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
var result = await MSBuildProcessManager.DotnetMSBuild(project);
Assert.BuildPassed(result);
- var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor", "blazor.satelliteasm.props");
- var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "ja", "standalone.resources.dll");
+ var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor.satelliteasm.props");
+ var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "ja", "blazorwasm.resources.dll");
var bootJson = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
result = await MSBuildProcessManager.DotnetMSBuild(project);
@@ -102,7 +103,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileDoesNotExist(result, satelliteAssemblyCacheFile);
Assert.FileDoesNotExist(result, satelliteAssemblyFile);
- var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
var satelliteResources = bootJsonFile.resources.satelliteResources;
Assert.Null(satelliteResources);
@@ -112,26 +113,26 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileExists(result, satelliteAssemblyCacheFile);
Assert.FileExists(result, satelliteAssemblyFile);
- bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
satelliteResources = bootJsonFile.resources.satelliteResources;
var kvp = Assert.Single(satelliteResources);
Assert.Equal("ja", kvp.Key);
- Assert.Equal("ja/standalone.resources.dll", Assert.Single(kvp.Value).Key);
+ Assert.Equal("ja/blazorwasm.resources.dll", Assert.Single(kvp.Value).Key);
}
[Fact]
public async Task Build_SatelliteAssembliesFileIsDeleted_IfAllSatelliteFilesAreRemoved()
{
// Arrange
- using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
File.Move(Path.Combine(project.DirectoryPath, "Resources.ja.resx.txt"), Path.Combine(project.DirectoryPath, "Resource.ja.resx"));
var result = await MSBuildProcessManager.DotnetMSBuild(project);
Assert.BuildPassed(result);
- var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor", "blazor.satelliteasm.props");
- var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "ja", "standalone.resources.dll");
+ var satelliteAssemblyCacheFile = Path.Combine(project.IntermediateOutputDirectory, "blazor.satelliteasm.props");
+ var satelliteAssemblyFile = Path.Combine(project.BuildOutputDirectory, "wwwroot", "_framework", "ja", "blazorwasm.resources.dll");
var bootJson = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
result = await MSBuildProcessManager.DotnetMSBuild(project);
@@ -139,11 +140,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileExists(result, satelliteAssemblyCacheFile);
Assert.FileExists(result, satelliteAssemblyFile);
- var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
var satelliteResources = bootJsonFile.resources.satelliteResources;
var kvp = Assert.Single(satelliteResources);
Assert.Equal("ja", kvp.Key);
- Assert.Equal("ja/standalone.resources.dll", Assert.Single(kvp.Value).Key);
+ Assert.Equal("ja/blazorwasm.resources.dll", Assert.Single(kvp.Value).Key);
File.Delete(Path.Combine(project.DirectoryPath, "Resource.ja.resx"));
@@ -151,7 +152,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.BuildPassed(result);
Assert.FileDoesNotExist(result, satelliteAssemblyCacheFile);
- bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
satelliteResources = bootJsonFile.resources.satelliteResources;
Assert.Null(satelliteResources);
}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs
new file mode 100644
index 0000000000..8788d9ce17
--- /dev/null
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs
@@ -0,0 +1,242 @@
+// 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.IO;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.Tasks;
+using Microsoft.AspNetCore.Testing;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
+{
+ public class WasmBuildIntegrationTest
+ {
+ private static readonly string DotNetJsFileName = $"dotnet.{BuildVariables.MicrosoftNETCoreAppRuntimeVersion}.js";
+
+ [Fact]
+ public async Task Build_Works()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+ Assert.BuildPassed(result);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", DotNetJsFileName);
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.pdb");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.pdb");
+
+ var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorwasm.StaticWebAssets.xml");
+ Assert.FileContains(result, staticWebAssets, Path.Combine(project.TargetFramework, "wwwroot"));
+ Assert.FileContains(result, staticWebAssets, Path.GetFullPath(Path.Combine(project.SolutionPath, "razorclasslibrary", "wwwroot")));
+ }
+
+ [Fact]
+ public async Task Build_InRelease_Works()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ project.Configuration = "Release";
+ var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+ Assert.BuildPassed(result);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", DotNetJsFileName);
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.pdb");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.pdb");
+
+ var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorwasm.StaticWebAssets.xml");
+ Assert.FileContains(result, staticWebAssets, Path.Combine(project.TargetFramework, "wwwroot"));
+ Assert.FileContains(result, staticWebAssets, Path.GetFullPath(Path.Combine(project.SolutionPath, "razorclasslibrary", "wwwroot")));
+ }
+
+ [Fact]
+ public async Task Build_ProducesBootJsonDataWithExpectedContent()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ project.Configuration = "Debug";
+ var wwwroot = Path.Combine(project.DirectoryPath, "wwwroot");
+ File.WriteAllText(Path.Combine(wwwroot, "appsettings.json"), "Default settings");
+ File.WriteAllText(Path.Combine(wwwroot, "appsettings.development.json"), "Development settings");
+
+ var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+ Assert.BuildPassed(result);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ var bootJsonData = ReadBootJsonData(result, bootJsonPath);
+
+ var runtime = bootJsonData.resources.runtime.Keys;
+ Assert.Contains(DotNetJsFileName, runtime);
+ Assert.Contains("dotnet.wasm", runtime);
+
+ var assemblies = bootJsonData.resources.assembly.Keys;
+ Assert.Contains("blazorwasm.dll", assemblies);
+ Assert.Contains("RazorClassLibrary.dll", assemblies);
+ Assert.Contains("System.Text.Json.dll", assemblies);
+
+ var pdb = bootJsonData.resources.pdb.Keys;
+ Assert.Contains("blazorwasm.pdb", pdb);
+ Assert.Contains("RazorClassLibrary.pdb", pdb);
+
+ Assert.Null(bootJsonData.resources.satelliteResources);
+
+ Assert.Contains("appsettings.json", bootJsonData.config);
+ Assert.Contains("appsettings.development.json", bootJsonData.config);
+ }
+
+ [Fact]
+ public async Task Build_InRelease_ProducesBootJsonDataWithExpectedContent()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ project.Configuration = "Release";
+ var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+ Assert.BuildPassed(result);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ var bootJsonData = ReadBootJsonData(result, bootJsonPath);
+
+ var runtime = bootJsonData.resources.runtime.Keys;
+ Assert.Contains(DotNetJsFileName, runtime);
+ Assert.Contains("dotnet.wasm", runtime);
+
+ var assemblies = bootJsonData.resources.assembly.Keys;
+ Assert.Contains("blazorwasm.dll", assemblies);
+ Assert.Contains("RazorClassLibrary.dll", assemblies);
+ Assert.Contains("System.Text.Json.dll", assemblies);
+
+ var pdb = bootJsonData.resources.pdb.Keys;
+ Assert.Contains("blazorwasm.pdb", pdb);
+ Assert.Contains("RazorClassLibrary.pdb", pdb);
+ Assert.Null(bootJsonData.resources.satelliteResources);
+ }
+
+ [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/22975")]
+ public async Task Build_WithBlazorEnableTimeZoneSupportDisabled_DoesNotCopyTimeZoneInfo()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ project.Configuration = "Release";
+ project.AddProjectFileContent(
+@"
+
+ false
+");
+
+ var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+ Assert.BuildPassed(result);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ var bootJsonData = ReadBootJsonData(result, bootJsonPath);
+
+ var runtime = bootJsonData.resources.runtime.Keys;
+ Assert.Contains("dotnet.wasm", runtime);
+ Assert.DoesNotContain("dotnet.timezones.dat", runtime);
+
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.wasm");
+ Assert.FileDoesNotExist(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.timezones.dat");
+ }
+
+ [Fact]
+ public async Task Build_Hosted_Works()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+ Assert.BuildPassed(result);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ Assert.FileDoesNotExist(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "blazorwasm.dll");
+
+ var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorhosted.StaticWebAssets.xml");
+ Assert.FileContains(result, staticWebAssets, Path.Combine("net5.0", "wwwroot"));
+ Assert.FileContains(result, staticWebAssets, Path.Combine("razorclasslibrary", "wwwroot"));
+ Assert.FileContains(result, staticWebAssets, Path.Combine("blazorwasm", "wwwroot"));
+ }
+
+ [Fact]
+ [QuarantinedTest]
+ public async Task Build_SatelliteAssembliesAreCopiedToBuildOutput()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary", "classlibrarywithsatelliteassemblies" });
+ project.AddProjectFileContent(
+@"
+
+ $(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies
+
+
+
+");
+ var resxfileInProject = Path.Combine(project.DirectoryPath, "Resources.ja.resx.txt");
+ File.Move(resxfileInProject, Path.Combine(project.DirectoryPath, "Resource.ja.resx"));
+
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/restore");
+
+ Assert.BuildPassed(result);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "classlibrarywithsatelliteassemblies.dll");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "Microsoft.CodeAnalysis.CSharp.dll");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output.
+
+ var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ Assert.FileContains(result, bootJsonPath, "\"Microsoft.CodeAnalysis.CSharp.dll\"");
+ Assert.FileContains(result, bootJsonPath, "\"fr\\/Microsoft.CodeAnalysis.CSharp.resources.dll\"");
+ }
+
+ [Fact]
+ public async Task Build_WithCustomOutputPath_Works()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+
+ project.AddDirectoryBuildContent(
+@"
+ $(MSBuildThisFileDirectory)build\bin\
+ $(MSBuildThisFileDirectory)build\obj\
+");
+
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/restore");
+ Assert.BuildPassed(result);
+ }
+
+ private static BootJsonData ReadBootJsonData(MSBuildResult result, string path)
+ {
+ return JsonSerializer.Deserialize(
+ File.ReadAllText(Path.Combine(result.Project.DirectoryPath, path)),
+ new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ }
+ }
+}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs
new file mode 100644
index 0000000000..674d180c88
--- /dev/null
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs
@@ -0,0 +1,173 @@
+// 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.IO;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
+{
+ public class WasmBuildLazyLoadTest
+ {
+ [Fact]
+ public async Task Build_LazyLoadExplicitAssembly_Debug_Works()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ project.Configuration = "Debug";
+
+ project.AddProjectFileContent(
+@"
+
+
+
+");
+
+ var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ // Verify that a blazor.boot.json file has been created
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ // And that the assembly is in the output
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll");
+
+ var bootJson = ReadBootJsonData(result, Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"));
+
+ // And that it has been labelled as a dynamic assembly in the boot.json
+ var dynamicAssemblies = bootJson.resources.dynamicAssembly;
+ var assemblies = bootJson.resources.assembly;
+
+ Assert.NotNull(dynamicAssemblies);
+ Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys);
+ Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys);
+
+ // App assembly should not be lazy loaded
+ Assert.DoesNotContain("blazorwasm.dll", dynamicAssemblies.Keys);
+ Assert.Contains("blazorwasm.dll", assemblies.Keys);
+ }
+
+ [Fact]
+ public async Task Build_LazyLoadExplicitAssembly_Release_Works()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ project.Configuration = "Release";
+
+ project.AddProjectFileContent(
+@"
+
+
+
+");
+
+ var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ // Verify that a blazor.boot.json file has been created
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ // And that the assembly is in the output
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll");
+
+ var bootJson = ReadBootJsonData(result, Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"));
+
+ // And that it has been labelled as a dynamic assembly in the boot.json
+ var dynamicAssemblies = bootJson.resources.dynamicAssembly;
+ var assemblies = bootJson.resources.assembly;
+
+ Assert.NotNull(dynamicAssemblies);
+ Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys);
+ Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys);
+
+ // App assembly should not be lazy loaded
+ Assert.DoesNotContain("blazorwasm.dll", dynamicAssemblies.Keys);
+ Assert.Contains("blazorwasm.dll", assemblies.Keys);
+ }
+
+ [Fact]
+ public async Task Publish_LazyLoadExplicitAssembly_Debug_Works()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ project.Configuration = "Debug";
+
+ project.AddProjectFileContent(
+@"
+
+
+
+");
+
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
+
+ var publishDirectory = project.PublishOutputDirectory;
+
+ // Verify that a blazor.boot.json file has been created
+ Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ // And that the assembly is in the output
+ Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll");
+
+ var bootJson = ReadBootJsonData(result, Path.Combine(publishDirectory, "wwwroot", "_framework", "blazor.boot.json"));
+
+ // And that it has been labelled as a dynamic assembly in the boot.json
+ var dynamicAssemblies = bootJson.resources.dynamicAssembly;
+ var assemblies = bootJson.resources.assembly;
+
+ Assert.NotNull(dynamicAssemblies);
+ Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys);
+ Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys);
+
+ // App assembly should not be lazy loaded
+ Assert.DoesNotContain("blazorwasm.dll", dynamicAssemblies.Keys);
+ Assert.Contains("blazorwasm.dll", assemblies.Keys);
+ }
+
+ [Fact]
+ public async Task Publish_LazyLoadExplicitAssembly_Release_Works()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ project.Configuration = "Release";
+
+ project.AddProjectFileContent(
+@"
+
+
+
+");
+
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
+
+ var publishDirectory = project.PublishOutputDirectory;
+
+ // Verify that a blazor.boot.json file has been created
+ Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ // And that the assembly is in the output
+ Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll");
+
+ var bootJson = ReadBootJsonData(result, Path.Combine(publishDirectory, "wwwroot", "_framework", "blazor.boot.json"));
+
+ // And that it has been labelled as a dynamic assembly in the boot.json
+ var dynamicAssemblies = bootJson.resources.dynamicAssembly;
+ var assemblies = bootJson.resources.assembly;
+
+ Assert.NotNull(dynamicAssemblies);
+ Assert.Contains("RazorClassLibrary.dll", dynamicAssemblies.Keys);
+ Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys);
+
+ // App assembly should not be lazy loaded
+ Assert.DoesNotContain("blazorwasm.dll", dynamicAssemblies.Keys);
+ Assert.Contains("blazorwasm.dll", assemblies.Keys);
+ }
+
+ private static BootJsonData ReadBootJsonData(MSBuildResult result, string path)
+ {
+ return JsonSerializer.Deserialize(
+ File.ReadAllText(Path.Combine(result.Project.DirectoryPath, path)),
+ new JsonSerializerOptions(JsonSerializerDefaults.Web));
+ }
+ }
+}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmCompressionTests.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmCompressionTests.cs
new file mode 100644
index 0000000000..e308d78274
--- /dev/null
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmCompressionTests.cs
@@ -0,0 +1,165 @@
+// 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.IO;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
+{
+ public class WasmCompressionTests
+ {
+ [Fact]
+ public async Task Publish_UpdatesFilesWhenSourcesChange()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish");
+
+ Assert.BuildPassed(result);
+
+ // Act
+ var mainAppDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll");
+ var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll);
+ var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll.br");
+ var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll);
+
+ var blazorBootJson = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ var blazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson);
+ var blazorBootJsonCompressed = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json.br");
+ var blazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed);
+
+ var programFile = Path.Combine(project.DirectoryPath, "..", "blazorwasm", "Program.cs");
+ var programFileContents = File.ReadAllText(programFile);
+ File.WriteAllText(programFile, programFileContents.Replace("args", "arguments"));
+
+ // Assert
+ result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish");
+ Assert.BuildPassed(result);
+ var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll);
+ var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll);
+ var newBlazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson);
+ var newBlazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed);
+
+ Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint);
+ Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint);
+
+ Assert.NotEqual(blazorBootJsonThumbPrint, newBlazorBootJsonThumbPrint);
+ Assert.NotEqual(blazorBootJsonCompressedThumbPrint, newBlazorBootJsonCompressedThumbPrint);
+ }
+
+ [Fact]
+ public async Task Publish_WithoutLinkerAndCompression_UpdatesFilesWhenSourcesChange()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish", args: "/p:BlazorWebAssemblyEnableLinking=false");
+
+ Assert.BuildPassed(result);
+
+ // Act
+ var mainAppDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll");
+ var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll);
+
+ var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll.br");
+ var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll);
+
+ var programFile = Path.Combine(project.DirectoryPath, "..", "blazorwasm", "Program.cs");
+ var programFileContents = File.ReadAllText(programFile);
+ File.WriteAllText(programFile, programFileContents.Replace("args", "arguments"));
+
+ // Assert
+ result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish", args: "/p:BlazorWebAssemblyEnableLinking=false");
+ Assert.BuildPassed(result);
+ var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll);
+ var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll);
+
+ Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint);
+ Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint);
+ }
+
+ [Fact]
+ public async Task Publish_WithLinkerAndCompression_IsIncremental()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish");
+
+ Assert.BuildPassed(result);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ // Act
+ var compressedFilesFolder = Path.Combine("..", "blazorwasm", project.IntermediateOutputDirectory, "brotli");
+ var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder);
+
+ // Assert
+ for (var i = 0; i < 3; i++)
+ {
+ result = await MSBuildProcessManager.DotnetMSBuild(project);
+ Assert.BuildPassed(result);
+
+ var newThumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder);
+ Assert.Equal(thumbPrint.Count, newThumbPrint.Count);
+ for (var j = 0; j < thumbPrint.Count; j++)
+ {
+ Assert.Equal(thumbPrint[j], newThumbPrint[j]);
+ }
+ }
+ }
+
+ [Fact]
+ public async Task Publish_WithoutLinkerAndCompression_IsIncremental()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish", args: "/p:BlazorWebAssemblyEnableLinking=false");
+
+ Assert.BuildPassed(result);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ // Act
+ var compressedFilesFolder = Path.Combine("..", "blazorwasm", project.IntermediateOutputDirectory, "brotli");
+ var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder);
+
+ // Assert
+ for (var i = 0; i < 3; i++)
+ {
+ result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorWebAssemblyEnableLinking=false");
+ Assert.BuildPassed(result);
+
+ var newThumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder);
+ Assert.Equal(thumbPrint.Count, newThumbPrint.Count);
+ for (var j = 0; j < thumbPrint.Count; j++)
+ {
+ Assert.Equal(thumbPrint[j], newThumbPrint[j]);
+ }
+ }
+ }
+
+ [Fact]
+ public async Task Publish_CompressesAllFrameworkFiles()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish");
+
+ Assert.BuildPassed(result);
+
+ var extensions = new[] { ".dll", ".js", ".pdb", ".wasm", ".map", ".json", ".dat" };
+
+ // Act
+ var frameworkFilesPath = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework");
+
+ foreach (var file in Directory.EnumerateFiles(frameworkFilesPath, "*", new EnumerationOptions { RecurseSubdirectories = true, }))
+ {
+ var extension = Path.GetExtension(file);
+ if (extension != ".br" && extension != ".gz")
+ {
+ Assert.FileExists(result, file + ".br");
+ }
+ }
+ }
+ }
+}
diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs
similarity index 65%
rename from src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs
rename to src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs
index b4b34470ea..dfaec7c803 100644
--- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs
@@ -1,28 +1,27 @@
// 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.IO;
-using System.Linq;
+using System.IO.Compression;
using System.Text.Json;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Blazor.Build;
+using Microsoft.AspNetCore.Razor.Tasks;
using Microsoft.AspNetCore.Testing;
using Xunit;
using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary;
-using static Microsoft.AspNetCore.Components.WebAssembly.Build.WebAssemblyRuntimePackage;
-using System.IO.Compression;
+using static Microsoft.AspNetCore.Razor.Design.IntegrationTests.ServiceWorkerAssert;
-namespace Microsoft.AspNetCore.Components.WebAssembly.Build
+namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{
- public class PublishIntegrationTest
+ public class WasmPublishIntegrationTest
{
+ private static readonly string DotNetJsFileName = $"dotnet.{BuildVariables.MicrosoftNETCoreAppRuntimeVersion}.js";
+
[Fact]
public async Task Publish_WithDefaultSettings_Works()
{
// Arrange
- using var project = ProjectDirectory.Create("standalone", additionalProjects: new [] { "razorclasslibrary", "LinkBaseToWebRoot" });
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary", "LinkBaseToWebRoot" });
project.Configuration = "Debug";
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
@@ -34,10 +33,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName);
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
// Verify referenced static web assets
Assert.FileExists(result, blazorPublishDirectory, "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
@@ -61,31 +60,29 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
serviceWorkerContent: "// This is the production service worker",
assetsManifestPath: "custom-service-worker-assets.js");
-
- // When publishing without linker, we expect to have collation information present in mscorlib.dll
- Assert.AssemblyContainsResource(result, Path.Combine(blazorPublishDirectory, "_framework", "_bin", "mscorlib.dll"), "collation.cjkCHS.bin");
}
[Fact]
public async Task Publish_InRelease_Works()
{
// Arrange
- using var project = ProjectDirectory.Create("standalone", additionalProjects: new [] { "razorclasslibrary", "LinkBaseToWebRoot" });
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary", "LinkBaseToWebRoot" });
project.Configuration = "Release";
- var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", "");
Assert.BuildPassed(result);
+
var publishDirectory = project.PublishOutputDirectory;
var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot");
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName);
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
// Verify referenced static web assets
Assert.FileExists(result, blazorPublishDirectory, "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
@@ -103,38 +100,13 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
// Verify web.config
Assert.FileExists(result, publishDirectory, "web.config");
Assert.FileCountEquals(result, 1, publishDirectory, "*", SearchOption.TopDirectoryOnly);
-
- // When publishing with linker, we expect to have collation information present in mscorlib.dll
- Assert.AssemblyContainsResource(result, Path.Combine(blazorPublishDirectory, "_framework", "_bin", "mscorlib.dll"), "collation.cjkCHS.bin");
- }
-
- [Fact]
- public async Task Publish_LinkerEnabledAndBlazorWebAssemblyPreserveCollationDataSet_CollationInformationIsPreserved()
- {
- // Arrange
- using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
- project.Configuration = "Release";
- project.AddProjectFileContent(
-@"
- false
-");
- var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
-
- Assert.BuildPassed(result);
-
- var publishDirectory = project.PublishOutputDirectory;
-
- var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot");
-
- // When publishing with BlazorWebAssemblyPreserveCollationData=false, collation information should be stripped out.
- Assert.AssemblyDoesNotContainResource(result, Path.Combine(blazorPublishDirectory, "_framework", "_bin", "mscorlib.dll"), "collation.cjkCHS.bin");
}
[Fact]
public async Task Publish_WithExistingWebConfig_Works()
{
// Arrange
- using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary", "LinkBaseToWebRoot" });
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary", "LinkBaseToWebRoot" });
project.Configuration = "Release";
var webConfigContents = "test webconfig contents";
@@ -155,7 +127,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public async Task Publish_WithNoBuild_Works()
{
// Arrange
- using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build");
Assert.BuildPassed(result);
@@ -169,10 +141,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName);
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
// Verify static assets are in the publish directory
Assert.FileExists(result, blazorPublishDirectory, "index.html");
@@ -209,13 +181,13 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
}
[Fact]
- public async Task Publish_WithLinkOnBuildDisabled_Works()
+ public async Task Publish_WithTrimmingdDisabled_Works()
{
// Arrange
- using var project = ProjectDirectory.Create("standalone", additionalProjects: new [] { "razorclasslibrary" });
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
project.AddProjectFileContent(
@"
- false
+ false
");
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
@@ -227,10 +199,14 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName);
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
+
+ // Verify compression works
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br"); //
// Verify static assets are in the publish directory
Assert.FileExists(result, blazorPublishDirectory, "index.html");
@@ -253,7 +229,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public async Task Publish_SatelliteAssemblies_AreCopiedToBuildOutput()
{
// Arrange
- using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary", "classlibrarywithsatelliteassemblies" });
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary", "classlibrarywithsatelliteassemblies" });
project.AddProjectFileContent(
@"
@@ -270,49 +246,44 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
var publishDirectory = project.PublishOutputDirectory;
var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.CodeAnalysis.CSharp.dll");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output.
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "Microsoft.CodeAnalysis.CSharp.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output.
var bootJsonPath = Path.Combine(blazorPublishDirectory, "_framework", "blazor.boot.json");
Assert.FileContains(result, bootJsonPath, "\"Microsoft.CodeAnalysis.CSharp.dll\"");
Assert.FileContains(result, bootJsonPath, "\"fr\\/Microsoft.CodeAnalysis.CSharp.resources.dll\"");
VerifyBootManifestHashes(result, blazorPublishDirectory);
- VerifyServiceWorkerFiles(result, blazorPublishDirectory,
- serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
- serviceWorkerContent: "// This is the production service worker",
- assetsManifestPath: "custom-service-worker-assets.js");
}
[Fact]
[QuarantinedTest]
- public async Task Publish_HostedApp_WithLinkOnBuildTrue_Works()
+ public async Task Publish_HostedApp_DefaultSettings_Works()
{
// Arrange
- using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", });
- project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework;
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", });
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
- AddSiblingProjectFileContent(project, @"
-
- true
-");
-
Assert.BuildPassed(result);
var publishDirectory = project.PublishOutputDirectory;
// Make sure the main project exists
Assert.FileExists(result, publishDirectory, "blazorhosted.dll");
+
+ // Verification for https://github.com/dotnet/aspnetcore/issues/19926. Verify binaries for projects
+ // referenced by the Hosted project appear in the publish directory
Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll");
+ Assert.FileExists(result, publishDirectory, "blazorwasm.dll");
var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot");
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName);
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
+
// Verify project references appear as static web assets
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "RazorClassLibrary.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll");
// Also verify project references to the server project appear in the publish output
Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll");
@@ -327,6 +298,13 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileExists(result, publishDirectory, "web.config");
VerifyBootManifestHashes(result, blazorPublishDirectory);
+
+ // Verify compression works
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
+
VerifyServiceWorkerFiles(result, blazorPublishDirectory,
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
serviceWorkerContent: "// This is the production service worker",
@@ -334,17 +312,82 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
}
[Fact]
- // Regression test for https://github.com/dotnet/aspnetcore/issues/18752
- public async Task Publish_HostedApp_WithLinkOnBuildFalse_Works()
+ public async Task Publish_HostedApp_ProducesBootJsonDataWithExpectedContent()
{
// Arrange
- using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", });
- project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework;
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", });
+ var wwwroot = Path.Combine(project.DirectoryPath, "..", "blazorwasm", "wwwroot");
+ File.WriteAllText(Path.Combine(wwwroot, "appsettings.json"), "Default settings");
+ File.WriteAllText(Path.Combine(wwwroot, "appsettings.development.json"), "Development settings");
- AddSiblingProjectFileContent(project, @"
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
+ Assert.BuildPassed(result);
+
+ var bootJsonPath = Path.Combine(project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ var bootJsonData = ReadBootJsonData(result, bootJsonPath);
+
+ var runtime = bootJsonData.resources.runtime.Keys;
+ Assert.Contains(DotNetJsFileName, runtime);
+ Assert.Contains("dotnet.wasm", runtime);
+
+ var assemblies = bootJsonData.resources.assembly.Keys;
+ Assert.Contains("blazorwasm.dll", assemblies);
+ Assert.Contains("RazorClassLibrary.dll", assemblies);
+ Assert.Contains("System.Text.Json.dll", assemblies);
+
+ // No pdbs
+ Assert.Null(bootJsonData.resources.pdb);
+ Assert.Null(bootJsonData.resources.satelliteResources);
+
+ Assert.Contains("appsettings.json", bootJsonData.config);
+ Assert.Contains("appsettings.development.json", bootJsonData.config);
+ }
+
+ [Fact]
+ public async Task Publish_HostedApp_WithSatelliteAssemblies()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", "classlibrarywithsatelliteassemblies", });
+ var wasmProject = project.GetSibling("blazorwasm");
+
+ wasmProject.AddProjectFileContent(
+@"
- false
-");
+ $(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies
+
+
+
+");
+ var resxfileInProject = Path.Combine(wasmProject.DirectoryPath, "Resources.ja.resx.txt");
+ File.Move(resxfileInProject, Path.Combine(wasmProject.DirectoryPath, "Resource.ja.resx"));
+
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", "/restore");
+ Assert.BuildPassed(result);
+
+ var bootJsonPath = Path.Combine(project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ var bootJsonData = ReadBootJsonData(result, bootJsonPath);
+
+ var publishOutputDirectory = project.PublishOutputDirectory;
+
+ Assert.FileExists(result, publishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, publishOutputDirectory, "wwwroot", "_framework", "classlibrarywithsatelliteassemblies.dll");
+ Assert.FileExists(result, publishOutputDirectory, "wwwroot", "_framework", "Microsoft.CodeAnalysis.CSharp.dll");
+ Assert.FileExists(result, publishOutputDirectory, "wwwroot", "_framework", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output.
+
+ Assert.FileContains(result, bootJsonPath, "\"Microsoft.CodeAnalysis.CSharp.dll\"");
+ Assert.FileContains(result, bootJsonPath, "\"fr\\/Microsoft.CodeAnalysis.CSharp.resources.dll\"");
+ }
+
+ [Fact]
+ // Regression test for https://github.com/dotnet/aspnetcore/issues/18752
+ public async Task Publish_HostedApp_WithoutTrimming_Works()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", });
+ AddWasmProjectContent(project, @"
+
+ false
+ ");
// VS builds projects individually and then a publish with BuildDependencies=false, but building the main project is a close enough approximation for this test.
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build");
@@ -361,16 +404,19 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
// Verification for https://github.com/dotnet/aspnetcore/issues/19926. Verify binaries for projects
// referenced by the Hosted project appear in the publish directory
Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll");
- Assert.FileExists(result, publishDirectory, "standalone.dll");
+ Assert.FileExists(result, publishDirectory, "blazorwasm.dll");
var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot");
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName);
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "RazorClassLibrary.dll");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
+
+ // Verify project references appear as static web assets
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll");
+ // Also verify project references to the server project appear in the publish output
+ Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll");
// Verify static assets are in the publish directory
Assert.FileExists(result, blazorPublishDirectory, "index.html");
@@ -379,26 +425,28 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css");
- // Verify static assets are in the publish directory
- Assert.FileExists(result, blazorPublishDirectory, "index.html");
-
// Verify web.config
Assert.FileExists(result, publishDirectory, "web.config");
VerifyBootManifestHashes(result, blazorPublishDirectory);
+
+ // Verify compression works
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
+
VerifyServiceWorkerFiles(result, blazorPublishDirectory,
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
serviceWorkerContent: "// This is the production service worker",
assetsManifestPath: "custom-service-worker-assets.js");
-
- }
+ }
[Fact]
public async Task Publish_HostedApp_WithNoBuild_Works()
{
// Arrange
- using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", });
- project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework;
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", });
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build");
Assert.BuildPassed(result);
@@ -411,11 +459,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot");
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName);
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
// Verify static assets are in the publish directory
Assert.FileExists(result, blazorPublishDirectory, "index.html");
@@ -439,8 +486,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
{
// Simulates publishing the same way VS does by setting BuildProjectReferences=false.
// Arrange
- using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", });
- project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework;
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", });
project.Configuration = "Release";
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build", "/p:BuildInsideVisualStudio=true");
@@ -454,11 +500,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot");
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", DotNetJsFileName);
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
// Verify static assets are in the publish directory
Assert.FileExists(result, blazorPublishDirectory, "index.html");
@@ -467,6 +512,12 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css");
+ // Verify compression works
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
+
// Verify web.config
Assert.FileExists(result, publishDirectory, "web.config");
@@ -484,26 +535,24 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
{
// Simulates publishing the same way VS does by setting BuildProjectReferences=false.
// Arrange
- var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary", "classlibrarywithsatelliteassemblies" });
- project.TargetFramework = TestFacts.DefaultNetCoreTargetFramework;
- project.Configuration = "Release";
- var standaloneProjectDirectory = Path.Combine(project.DirectoryPath, "..", "standalone");
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", "classlibrarywithsatelliteassemblies" });
+ var blazorwasmProjectDirectory = Path.Combine(project.DirectoryPath, "..", "blazorwasm");
- // Setup the standalone app to have it's own satellite assemblies as well as transitively imported satellite assemblies.
- var resxfileInProject = Path.Combine(standaloneProjectDirectory , "Resources.ja.resx.txt");
- File.Move(resxfileInProject, Path.Combine(standaloneProjectDirectory, "Resource.ja.resx"));
+ // Setup the blazorwasm app to have it's own satellite assemblies as well as transitively imported satellite assemblies.
+ var resxfileInProject = Path.Combine(blazorwasmProjectDirectory, "Resources.ja.resx.txt");
+ File.Move(resxfileInProject, Path.Combine(blazorwasmProjectDirectory, "Resource.ja.resx"));
- var standaloneProjFile = Path.Combine(standaloneProjectDirectory, "standalone.csproj");
- var existing = File.ReadAllText(standaloneProjFile);
+ var blazorwasmProjFile = Path.Combine(blazorwasmProjectDirectory, "blazorwasm.csproj");
+ var existing = File.ReadAllText(blazorwasmProjFile);
var updatedContent = @"
-
- $(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies
-
-
-
-";
+
+ $(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies
+
+
+
+ ";
var updated = existing.Replace("", updatedContent);
- File.WriteAllText(standaloneProjFile, updated);
+ File.WriteAllText(blazorwasmProjFile, updated);
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build", $"/restore");
@@ -517,11 +566,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot");
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "ja", "standalone.resources.dll");
- Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "fr", "Microsoft.CodeAnalysis.resources.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "ja", "blazorwasm.resources.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "fr", "Microsoft.CodeAnalysis.resources.dll");
var bootJson = Path.Combine(project.DirectoryPath, blazorPublishDirectory, "_framework", "blazor.boot.json");
- var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
var satelliteResources = bootJsonFile.resources.satelliteResources;
Assert.Contains("es-ES", satelliteResources.Keys);
@@ -529,7 +578,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.Contains("fr", satelliteResources.Keys);
Assert.Contains("fr/Microsoft.CodeAnalysis.CSharp.resources.dll", satelliteResources["fr"].Keys);
Assert.Contains("ja", satelliteResources.Keys);
- Assert.Contains("ja/standalone.resources.dll", satelliteResources["ja"].Keys);
+ Assert.Contains("ja/blazorwasm.resources.dll", satelliteResources["ja"].Keys);
VerifyServiceWorkerFiles(result, blazorPublishDirectory,
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
@@ -537,9 +586,64 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
assetsManifestPath: "custom-service-worker-assets.js");
}
- private static void AddSiblingProjectFileContent(ProjectDirectory project, string content)
+ [Fact]
+ public async Task Publish_HostedApp_WithRid_Works()
{
- var path = Path.Combine(project.SolutionPath, "standalone", "standalone.csproj");
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorhosted-rid", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", });
+ project.RuntimeIdentifier = "linux-x64";
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish");
+
+ Assert.BuildPassed(result);
+
+ var publishDirectory = project.PublishOutputDirectory;
+ // Make sure the main project exists
+ Assert.FileExists(result, publishDirectory, "blazorhosted-rid.dll");
+ Assert.FileExists(result, publishDirectory, "libhostfxr.so"); // Verify that we're doing a self-contained deployment
+
+ Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll");
+ Assert.FileExists(result, publishDirectory, "blazorwasm.dll");
+
+ var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName);
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
+
+ // Verify project references appear as static web assets
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll");
+ // Also verify project references to the server project appear in the publish output
+ Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll");
+
+ // Verify static assets are in the publish directory
+ Assert.FileExists(result, blazorPublishDirectory, "index.html");
+
+ // Verify static web assets from referenced projects are copied.
+ Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
+ Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css");
+
+ // Verify web.config
+ Assert.FileExists(result, publishDirectory, "web.config");
+
+ VerifyBootManifestHashes(result, blazorPublishDirectory);
+
+ // Verify compression works
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
+
+ VerifyServiceWorkerFiles(result, blazorPublishDirectory,
+ serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
+ serviceWorkerContent: "// This is the production service worker",
+ assetsManifestPath: "custom-service-worker-assets.js");
+ }
+
+ private static void AddWasmProjectContent(ProjectDirectory project, string content)
+ {
+ var path = Path.Combine(project.SolutionPath, "blazorwasm", "blazorwasm.csproj");
var existing = File.ReadAllText(path);
var updated = existing.Replace("", content);
File.WriteAllText(path, updated);
@@ -555,102 +659,43 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
{
var bootManifestResolvedPath = Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
var bootManifestJson = File.ReadAllText(bootManifestResolvedPath);
- var bootManifest = JsonSerializer.Deserialize(bootManifestJson);
+ var bootManifest = JsonSerializer.Deserialize(bootManifestJson);
- VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.assembly, r => $"_framework/_bin/{r}");
- VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.runtime, r => $"_framework/wasm/{r}");
+ VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.assembly);
+ VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.runtime);
if (bootManifest.resources.pdb != null)
{
- VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.pdb, r => $"_framework/_bin/{r}");
+ VerifyBootManifestHashes(result, blazorPublishDirectory, bootManifest.resources.pdb);
}
if (bootManifest.resources.satelliteResources != null)
{
foreach (var resourcesForCulture in bootManifest.resources.satelliteResources.Values)
{
- VerifyBootManifestHashes(result, blazorPublishDirectory, resourcesForCulture, r => $"_framework/_bin/{r}");
+ VerifyBootManifestHashes(result, blazorPublishDirectory, resourcesForCulture);
}
}
- }
- private static void VerifyBootManifestHashes(MSBuildResult result, string blazorPublishDirectory, ResourceHashesByNameDictionary resources, Func relativePathFunc)
- {
- foreach (var (name, hash) in resources)
+ static void VerifyBootManifestHashes(MSBuildResult result, string blazorPublishDirectory, ResourceHashesByNameDictionary resources)
{
- var relativePath = Path.Combine(blazorPublishDirectory, relativePathFunc(name));
- Assert.FileHashEquals(result, relativePath, ParseWebFormattedHash(hash));
- }
- }
-
- private static void VerifyServiceWorkerFiles(MSBuildResult result, string blazorPublishDirectory, string serviceWorkerPath, string serviceWorkerContent, string assetsManifestPath)
- {
- // Check the expected files are there
- var serviceWorkerResolvedPath = Assert.FileExists(result, blazorPublishDirectory, serviceWorkerPath);
- var assetsManifestResolvedPath = Assert.FileExists(result, blazorPublishDirectory, assetsManifestPath);
-
- // Check the service worker contains the expected content (which comes from the PublishedContent file)
- Assert.FileContains(result, serviceWorkerResolvedPath, serviceWorkerContent);
-
- // Check the assets manifest version was added to the published service worker
- var assetsManifest = ReadServiceWorkerAssetsManifest(assetsManifestResolvedPath);
- Assert.FileContains(result, serviceWorkerResolvedPath, $"/* Manifest version: {assetsManifest.version} */");
-
- // Check the assets manifest contains correct entries for all static content we're publishing
- var resolvedPublishDirectory = Path.Combine(result.Project.DirectoryPath, blazorPublishDirectory);
- var publishedStaticFiles = Directory.GetFiles(resolvedPublishDirectory, "*", new EnumerationOptions { RecurseSubdirectories = true });
- var assetsManifestHashesByUrl = (IReadOnlyDictionary)assetsManifest.assets.ToDictionary(x => x.url, x => x.hash);
- foreach (var publishedFilePath in publishedStaticFiles)
- {
- var publishedFileRelativePath = Path.GetRelativePath(resolvedPublishDirectory, publishedFilePath);
-
- // We don't list compressed files in the SWAM, as these are transparent to the client,
- // nor do we list the service worker itself or its assets manifest, as these don't need to be fetched in the same way
- if (IsCompressedFile(publishedFileRelativePath)
- || string.Equals(publishedFileRelativePath, serviceWorkerPath, StringComparison.Ordinal)
- || string.Equals(publishedFileRelativePath, assetsManifestPath, StringComparison.Ordinal))
+ foreach (var (name, hash) in resources)
{
- continue;
+ var relativePath = Path.Combine(blazorPublishDirectory, "_framework", name);
+ Assert.FileHashEquals(result, relativePath, ParseWebFormattedHash(hash));
}
-
- // Verify hash
- var publishedFileUrl = publishedFileRelativePath.Replace('\\', '/');
- var expectedHash = ParseWebFormattedHash(assetsManifestHashesByUrl[publishedFileUrl]);
- Assert.Contains(publishedFileUrl, assetsManifestHashesByUrl);
- Assert.FileHashEquals(result, publishedFilePath, expectedHash);
}
- }
- private static string ParseWebFormattedHash(string webFormattedHash)
- {
- Assert.StartsWith("sha256-", webFormattedHash);
- return webFormattedHash.Substring(7);
- }
-
- private static bool IsCompressedFile(string path)
- {
- switch (Path.GetExtension(path))
+ static string ParseWebFormattedHash(string webFormattedHash)
{
- case ".br":
- case ".gz":
- return true;
- default:
- return false;
+ Assert.StartsWith("sha256-", webFormattedHash);
+ return webFormattedHash.Substring(7);
}
}
- private static GenerateServiceWorkerAssetsManifest.AssetsManifestFile ReadServiceWorkerAssetsManifest(string assetsManifestResolvedPath)
+ private static BootJsonData ReadBootJsonData(MSBuildResult result, string path)
{
- var jsContents = File.ReadAllText(assetsManifestResolvedPath);
- var jsonStart = jsContents.IndexOf("{");
- var jsonLength = jsContents.LastIndexOf("}") - jsonStart + 1;
- var json = jsContents.Substring(jsonStart, jsonLength);
- return JsonSerializer.Deserialize(json);
- }
-
- private static GenerateBlazorBootJson.BootJsonData ReadBootJsonData(MSBuildResult result, string path)
- {
- return JsonSerializer.Deserialize(
+ return JsonSerializer.Deserialize(
File.ReadAllText(Path.Combine(result.Project.DirectoryPath, path)),
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPwaManifestTests.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPwaManifestTests.cs
new file mode 100644
index 0000000000..9bf091533b
--- /dev/null
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPwaManifestTests.cs
@@ -0,0 +1,211 @@
+// 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.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Xunit;
+using static Microsoft.AspNetCore.Razor.Design.IntegrationTests.ServiceWorkerAssert;
+
+
+namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
+{
+ public class WasmPwaManifestTests
+ {
+ private static readonly string DotNetJsFileName = $"dotnet.{BuildVariables.MicrosoftNETCoreAppRuntimeVersion}.js";
+
+ [Fact]
+ public async Task Build_ServiceWorkerAssetsManifest_Works()
+ {
+ // Arrange
+ var expectedExtensions = new[] { ".dll", ".pdb", ".js", ".wasm" };
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:ServiceWorkerAssetsManifest=service-worker-assets.js");
+
+ Assert.BuildPassed(result);
+
+ var buildOutputDirectory = project.BuildOutputDirectory;
+
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm");
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", DotNetJsFileName);
+ Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll");
+
+ var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorwasm.StaticWebAssets.xml");
+ Assert.FileContains(result, staticWebAssets, Path.Combine(project.TargetFramework, "wwwroot"));
+
+ var serviceWorkerAssetsManifest = Assert.FileExists(result, buildOutputDirectory, "wwwroot", "service-worker-assets.js");
+ // Trim prefix 'self.assetsManifest = ' and suffix ';'
+ var manifestContents = File.ReadAllText(serviceWorkerAssetsManifest).TrimEnd()[22..^1];
+
+ var manifestContentsJson = JsonDocument.Parse(manifestContents);
+ Assert.True(manifestContentsJson.RootElement.TryGetProperty("assets", out var assets));
+ Assert.Equal(JsonValueKind.Array, assets.ValueKind);
+
+ var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray();
+ Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e)));
+
+ VerifyServiceWorkerFiles(result,
+ Path.Combine(buildOutputDirectory, "wwwroot"),
+ serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
+ serviceWorkerContent: "// This is the development service worker",
+ assetsManifestPath: "service-worker-assets.js");
+ }
+
+ [Fact]
+ public async Task Build_HostedAppWithServiceWorker_Works()
+ {
+ // Arrange
+ var expectedExtensions = new[] { ".dll", ".pdb", ".js", ".wasm" };
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project);
+
+ Assert.BuildPassed(result);
+
+ var wasmProject = project.GetSibling("blazorwasm");
+ var buildOutputDirectory = Path.Combine(wasmProject.DirectoryPath, wasmProject.BuildOutputDirectory);
+
+ var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorwasm.StaticWebAssets.xml");
+ Assert.FileContains(result, staticWebAssets, Path.Combine(project.TargetFramework, "wwwroot"));
+
+ var serviceWorkerAssetsManifest = Assert.FileExists(result, buildOutputDirectory, "wwwroot", "custom-service-worker-assets.js");
+ // Trim prefix 'self.assetsManifest = ' and suffix ';'
+ var manifestContents = File.ReadAllText(serviceWorkerAssetsManifest).TrimEnd()[22..^1];
+
+ var manifestContentsJson = JsonDocument.Parse(manifestContents);
+ Assert.True(manifestContentsJson.RootElement.TryGetProperty("assets", out var assets));
+ Assert.Equal(JsonValueKind.Array, assets.ValueKind);
+
+ var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray();
+ Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e)));
+ }
+
+ [Fact]
+ public async Task PublishWithPWA_ProducesAssets()
+ {
+ // Arrange
+ var expectedExtensions = new[] { ".dll", ".pdb", ".js", ".wasm" };
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "Publish");
+
+ Assert.BuildPassed(result);
+
+ var publishOutputDirectory = project.PublishOutputDirectory;
+
+ var serviceWorkerAssetsManifest = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "custom-service-worker-assets.js");
+ // Trim prefix 'self.assetsManifest = ' and suffix ';'
+ var manifestContents = File.ReadAllText(serviceWorkerAssetsManifest).TrimEnd()[22..^1];
+
+ var manifestContentsJson = JsonDocument.Parse(manifestContents);
+ Assert.True(manifestContentsJson.RootElement.TryGetProperty("assets", out var assets));
+ Assert.Equal(JsonValueKind.Array, assets.ValueKind);
+
+ var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray();
+ Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e)));
+
+ var serviceWorkerFile = Path.Combine(publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js");
+ Assert.FileContainsLine(result, serviceWorkerFile, "// This is the production service worker");
+ }
+
+ [Fact]
+ public async Task PublishHostedWithPWA_ProducesAssets()
+ {
+ // Arrange
+ var expectedExtensions = new[] { ".dll", ".pdb", ".js", ".wasm" };
+ using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "Publish");
+
+ Assert.BuildPassed(result);
+
+ var publishOutputDirectory = project.PublishOutputDirectory;
+
+ var serviceWorkerAssetsManifest = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "custom-service-worker-assets.js");
+ // Trim prefix 'self.assetsManifest = ' and suffix ';'
+ var manifestContents = File.ReadAllText(serviceWorkerAssetsManifest).TrimEnd()[22..^1];
+
+ var manifestContentsJson = JsonDocument.Parse(manifestContents);
+ Assert.True(manifestContentsJson.RootElement.TryGetProperty("assets", out var assets));
+ Assert.Equal(JsonValueKind.Array, assets.ValueKind);
+
+ var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray();
+ Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e)));
+
+ var serviceWorkerFile = Path.Combine(publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js");
+ Assert.FileContainsLine(result, serviceWorkerFile, "// This is the production service worker");
+ }
+
+ [Fact]
+ public async Task Publish_UpdatesServiceWorkerVersionHash_WhenSourcesChange()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:initial.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
+
+ Assert.BuildPassed(result);
+
+ var publishOutputDirectory = project.PublishOutputDirectory;
+
+ var serviceWorkerFile = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js");
+ var version = File.ReadAllLines(serviceWorkerFile).Last();
+ var match = Regex.Match(version, "\\/\\* Manifest version: (.{8}) \\*\\/");
+ Assert.True(match.Success);
+ Assert.Equal(2, match.Groups.Count);
+ Assert.NotNull(match.Groups[1].Value);
+ var capture = match.Groups[1].Value;
+
+ // Act
+ var cssFile = Path.Combine(project.DirectoryPath, "LinkToWebRoot", "css", "app.css");
+ File.WriteAllText(cssFile, ".updated { }");
+
+ // Assert
+ result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:updated.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
+
+ Assert.BuildPassed(result);
+
+ var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last();
+ var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/");
+ Assert.True(updatedMatch.Success);
+ Assert.Equal(2, updatedMatch.Groups.Count);
+ Assert.NotNull(updatedMatch.Groups[1].Value);
+ var updatedCapture = updatedMatch.Groups[1].Value;
+
+ Assert.NotEqual(capture, updatedCapture);
+ }
+
+ [Fact]
+ public async Task Publish_DeterministicAcrossBuilds_WhenNoSourcesChange()
+ {
+ // Arrange
+ using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:initial.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
+
+ Assert.BuildPassed(result);
+
+ var publishOutputDirectory = project.PublishOutputDirectory;
+
+ var serviceWorkerFile = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js");
+ var version = File.ReadAllLines(serviceWorkerFile).Last();
+ var match = Regex.Match(version, "\\/\\* Manifest version: (.{8}) \\*\\/");
+ Assert.True(match.Success, $"No match found for manifest version regex in line: {version}");
+ Assert.Equal(2, match.Groups.Count);
+ Assert.NotNull(match.Groups[1].Value);
+ var capture = match.Groups[1].Value;
+
+ // Act && Assert
+ result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:updated.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
+
+ Assert.BuildPassed(result);
+
+ var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last();
+ var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/");
+ Assert.True(updatedMatch.Success);
+ Assert.Equal(2, updatedMatch.Groups.Count);
+ Assert.NotNull(updatedMatch.Groups[1].Value);
+ var updatedCapture = updatedMatch.Groups[1].Value;
+
+ Assert.Equal(capture, updatedCapture);
+ }
+ }
+}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/AssetsManifestFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/AssetsManifestFile.cs
new file mode 100644
index 0000000000..34dde41812
--- /dev/null
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/AssetsManifestFile.cs
@@ -0,0 +1,33 @@
+// 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.
+
+namespace Microsoft.AspNetCore.Razor.Tasks
+{
+#pragma warning disable IDE1006 // Naming Styles
+ public class AssetsManifestFile
+ {
+ ///
+ /// Gets or sets a version string.
+ ///
+ public string version { get; set; }
+
+ ///
+ /// Gets or sets the assets. Keys are URLs; values are base-64-formatted SHA256 content hashes.
+ ///
+ public AssetsManifestFileEntry[] assets { get; set; }
+ }
+
+ public class AssetsManifestFileEntry
+ {
+ ///
+ /// Gets or sets the asset URL. Normally this will be relative to the application's base href.
+ ///
+ public string url { get; set; }
+
+ ///
+ /// Gets or sets the file content hash. This should be the base-64-formatted SHA256 value.
+ ///
+ public string hash { get; set; }
+ }
+#pragma warning restore IDE1006 // Naming Styles
+}
diff --git a/src/Components/WebAssembly/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorReadSatelliteAssemblyFile.cs
similarity index 95%
rename from src/Components/WebAssembly/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs
rename to src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorReadSatelliteAssemblyFile.cs
index 18a3fc8214..f2f95dbf26 100644
--- a/src/Components/WebAssembly/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorReadSatelliteAssemblyFile.cs
@@ -6,7 +6,7 @@ using System.Xml.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-namespace Microsoft.AspNetCore.Components.WebAssembly.Build
+namespace Microsoft.AspNetCore.Razor.Tasks
{
public class BlazorReadSatelliteAssemblyFile : Task
{
diff --git a/src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorWriteSatelliteAssemblyFile.cs
similarity index 96%
rename from src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs
rename to src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorWriteSatelliteAssemblyFile.cs
index e1e6a8150e..da07cf6d00 100644
--- a/src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/BlazorWriteSatelliteAssemblyFile.cs
@@ -7,7 +7,7 @@ using System.Xml.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-namespace Microsoft.AspNetCore.Components.WebAssembly.Build
+namespace Microsoft.AspNetCore.Razor.Tasks
{
public class BlazorWriteSatelliteAssemblyFile : Task
{
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/BootJsonData.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/BootJsonData.cs
new file mode 100644
index 0000000000..0952070c8d
--- /dev/null
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/BootJsonData.cs
@@ -0,0 +1,85 @@
+// 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.Collections.Generic;
+using System.Runtime.Serialization;
+using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary;
+
+namespace Microsoft.AspNetCore.Razor.Tasks
+{
+#pragma warning disable IDE1006 // Naming Styles
+ ///
+ /// Defines the structure of a Blazor boot JSON file
+ ///
+ public class BootJsonData
+ {
+ ///
+ /// Gets the name of the assembly with the application entry point
+ ///
+ public string entryAssembly { get; set; }
+
+ ///
+ /// Gets the set of resources needed to boot the application. This includes the transitive
+ /// closure of .NET assemblies (including the entrypoint assembly), the dotnet.wasm file,
+ /// and any PDBs to be loaded.
+ ///
+ /// Within , dictionary keys are resource names,
+ /// and values are SHA-256 hashes formatted in prefixed base-64 style (e.g., 'sha256-abcdefg...')
+ /// as used for subresource integrity checking.
+ ///
+ public ResourcesData resources { get; set; } = new ResourcesData();
+
+ ///
+ /// Gets a value that determines whether to enable caching of the
+ /// inside a CacheStorage instance within the browser.
+ ///
+ public bool cacheBootResources { get; set; }
+
+ ///
+ /// Gets a value that determines if this is a debug build.
+ ///
+ public bool debugBuild { get; set; }
+
+ ///
+ /// Gets a value that determines if the linker is enabled.
+ ///
+ public bool linkerEnabled { get; set; }
+
+ ///
+ /// Config files for the application
+ ///
+ public List config { get; set; }
+ }
+
+ public class ResourcesData
+ {
+ ///
+ /// .NET Wasm runtime resources (dotnet.wasm, dotnet.js) etc.
+ ///
+ public ResourceHashesByNameDictionary runtime { get; set; } = new ResourceHashesByNameDictionary();
+
+ ///
+ /// "assembly" (.dll) resources
+ ///
+ public ResourceHashesByNameDictionary assembly { get; set; } = new ResourceHashesByNameDictionary();
+
+ ///
+ /// "debug" (.pdb) resources
+ ///
+ [DataMember(EmitDefaultValue = false)]
+ public ResourceHashesByNameDictionary pdb { get; set; }
+
+ ///
+ /// localization (.satellite resx) resources
+ ///
+ [DataMember(EmitDefaultValue = false)]
+ public Dictionary satelliteResources { get; set; }
+
+ ///
+ /// Assembly (.dll) resources that are loaded dynamically during runtime
+ ///
+ [DataMember(EmitDefaultValue = false)]
+ public ResourceHashesByNameDictionary dynamicAssembly { get; set; }
+ }
+#pragma warning restore IDE1006 // Naming Styles
+}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/BrotliCompress.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/BrotliCompress.cs
new file mode 100644
index 0000000000..9019f0520f
--- /dev/null
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/BrotliCompress.cs
@@ -0,0 +1,99 @@
+// 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.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.AspNetCore.Razor.Tasks
+{
+ public class BrotliCompress : DotNetToolTask
+ {
+ private static readonly char[] InvalidPathChars = Path.GetInvalidFileNameChars();
+
+ [Required]
+ public ITaskItem[] FilesToCompress { get; set; }
+
+ [Output]
+ public ITaskItem[] CompressedFiles { get; set; }
+
+ [Required]
+ public string OutputDirectory { get; set; }
+
+ public string CompressionLevel { get; set; }
+
+ public bool SkipIfOutputIsNewer { get; set; }
+
+ internal override string Command => "brotli";
+
+ protected override string GenerateResponseFileCommands()
+ {
+ var builder = new StringBuilder();
+
+ builder.AppendLine(Command);
+
+ if (!string.IsNullOrEmpty(CompressionLevel))
+ {
+ builder.AppendLine("-c");
+ builder.AppendLine(CompressionLevel);
+ }
+
+ CompressedFiles = new ITaskItem[FilesToCompress.Length];
+
+ for (var i = 0; i < FilesToCompress.Length; i++)
+ {
+ var input = FilesToCompress[i];
+ var inputFullPath = input.GetMetadata("FullPath");
+ var relativePath = input.GetMetadata("RelativePath");
+ var outputRelativePath = Path.Combine(OutputDirectory, CalculateTargetPath(relativePath));
+
+ var outputItem = new TaskItem(outputRelativePath);
+ input.CopyMetadataTo(outputItem);
+ // Relative path in the publish dir
+ outputItem.SetMetadata("RelativePath", relativePath + ".br");
+ CompressedFiles[i] = outputItem;
+
+ var outputFullPath = Path.GetFullPath(outputRelativePath);
+
+ if (SkipIfOutputIsNewer && File.Exists(outputFullPath) && File.GetLastWriteTimeUtc(inputFullPath) < File.GetLastWriteTimeUtc(outputFullPath))
+ {
+ Log.LogMessage(MessageImportance.Low, $"Skipping compression for '{input.ItemSpec}' because '{outputRelativePath}' is newer than '{input.ItemSpec}'.");
+ continue;
+ }
+
+ builder.AppendLine("-s");
+ builder.AppendLine(inputFullPath);
+
+ builder.AppendLine("-o");
+ builder.AppendLine(outputFullPath);
+ }
+
+ return builder.ToString();
+ }
+
+ private static string CalculateTargetPath(string relativePath)
+ {
+ // RelativePath can be long and if used as-is to write the output, might result in long path issues on Windows.
+ // Instead we'll calculate a fixed length path by hashing the input file name. This uses SHA1 similar to the Hash task in MSBuild
+ // since it has no crytographic significance.
+ using var hash = SHA1.Create();
+ var bytes = Encoding.UTF8.GetBytes(relativePath);
+ var hashString = Convert.ToBase64String(hash.ComputeHash(bytes));
+
+ var builder = new StringBuilder();
+
+ for (var i = 0; i < 8; i++)
+ {
+ var c = hashString[i];
+ builder.Append(InvalidPathChars.Contains(c) ? '+' : c);
+ }
+
+ builder.Append(".br");
+ return builder.ToString();
+ }
+ }
+}
diff --git a/src/Components/WebAssembly/Build/src/Tasks/BlazorCreateRootDescriptorFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs
similarity index 50%
rename from src/Components/WebAssembly/Build/src/Tasks/BlazorCreateRootDescriptorFile.cs
rename to src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs
index 45359bdb8a..57fa43a979 100644
--- a/src/Components/WebAssembly/Build/src/Tasks/BlazorCreateRootDescriptorFile.cs
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs
@@ -3,42 +3,53 @@
using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-namespace Microsoft.AspNetCore.Components.WebAssembly.Build
+namespace Microsoft.AspNetCore.Razor.Tasks
{
// Based on https://github.com/mono/linker/blob/3b329b9481e300bcf4fb88a2eebf8cb5ef8b323b/src/ILLink.Tasks/CreateRootDescriptorFile.cs
- public class BlazorCreateRootDescriptorFile : Task
+ public class CreateBlazorTrimmerRootDescriptorFile : Task
{
[Required]
- public ITaskItem[] AssemblyNames { get; set; }
+ public ITaskItem[] Assemblies { get; set; }
[Required]
- public ITaskItem RootDescriptorFilePath { get; set; }
+ public ITaskItem TrimmerFile { get; set; }
public override bool Execute()
{
- using var fileStream = File.Create(RootDescriptorFilePath.ItemSpec);
- var assemblyNames = AssemblyNames.Select(a => a.ItemSpec);
+ using var fileStream = File.Create(TrimmerFile.ItemSpec);
- WriteRootDescriptor(fileStream, assemblyNames);
+ WriteRootDescriptor(fileStream);
return true;
}
- internal static void WriteRootDescriptor(Stream stream, IEnumerable assemblyNames)
+ internal void WriteRootDescriptor(Stream stream)
{
var roots = new XElement("linker");
- foreach (var assemblyName in assemblyNames)
+ foreach (var assembly in Assemblies)
{
+ var assemblyName = assembly.GetMetadata("FileName") + assembly.GetMetadata("Extension");
+ var typePreserved = assembly.GetMetadata("Preserve");
+ var typeRequired = assembly.GetMetadata("Required");
+
+ var attributes = new List
+ {
+ new XAttribute("fullname", "*"),
+ new XAttribute("required", typeRequired),
+ };
+
+ if (!string.IsNullOrEmpty(typePreserved))
+ {
+ attributes.Add(new XAttribute("preserve", typePreserved));
+ }
+
roots.Add(new XElement("assembly",
new XAttribute("fullname", assemblyName),
- new XElement("type",
- new XAttribute("fullname", "*"),
- new XAttribute("required", "true"))));
+ new XElement("type", attributes)));
}
var xmlWriterSettings = new XmlWriterSettings
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateBlazorWebAssemblyBootJson.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateBlazorWebAssemblyBootJson.cs
new file mode 100644
index 0000000000..4d2c786443
--- /dev/null
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateBlazorWebAssemblyBootJson.cs
@@ -0,0 +1,155 @@
+// 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.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization.Json;
+using System.Text;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary;
+
+namespace Microsoft.AspNetCore.Razor.Tasks
+{
+ public class GenerateBlazorWebAssemblyBootJson : Task
+ {
+ [Required]
+ public string AssemblyPath { get; set; }
+
+ [Required]
+ public ITaskItem[] Resources { get; set; }
+
+ [Required]
+ public bool DebugBuild { get; set; }
+
+ [Required]
+ public bool LinkerEnabled { get; set; }
+
+ [Required]
+ public bool CacheBootResources { get; set; }
+
+ public ITaskItem[] ConfigurationFiles { get; set; }
+
+ [Required]
+ public string OutputPath { get; set; }
+
+ public ITaskItem[] LazyLoadedAssemblies { get; set; }
+
+ public override bool Execute()
+ {
+ using var fileStream = File.Create(OutputPath);
+ var entryAssemblyName = AssemblyName.GetAssemblyName(AssemblyPath).Name;
+
+ try
+ {
+ WriteBootJson(fileStream, entryAssemblyName);
+ }
+ catch (Exception ex)
+ {
+ Log.LogErrorFromException(ex);
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ // Internal for tests
+ public void WriteBootJson(Stream output, string entryAssemblyName)
+ {
+ var result = new BootJsonData
+ {
+ entryAssembly = entryAssemblyName,
+ cacheBootResources = CacheBootResources,
+ debugBuild = DebugBuild,
+ linkerEnabled = LinkerEnabled,
+ resources = new ResourcesData(),
+ config = new List(),
+ };
+
+ // Build a two-level dictionary of the form:
+ // - assembly:
+ // - UriPath (e.g., "System.Text.Json.dll")
+ // - ContentHash (e.g., "4548fa2e9cf52986")
+ // - runtime:
+ // - UriPath (e.g., "dotnet.js")
+ // - ContentHash (e.g., "3448f339acf512448")
+ if (Resources != null)
+ {
+ var resourceData = result.resources;
+ foreach (var resource in Resources)
+ {
+ ResourceHashesByNameDictionary resourceList;
+
+ var fileName = resource.GetMetadata("FileName");
+ var extension = resource.GetMetadata("Extension");
+ var resourceCulture = resource.GetMetadata("Culture");
+ var assetType = resource.GetMetadata("AssetType");
+ var resourceName = $"{fileName}{extension}";
+
+ if (IsLazyLoadedAssembly(fileName))
+ {
+ resourceData.dynamicAssembly ??= new ResourceHashesByNameDictionary();
+ resourceList = resourceData.dynamicAssembly;
+ }
+ else if (!string.IsNullOrEmpty(resourceCulture))
+ {
+ resourceData.satelliteResources ??= new Dictionary(StringComparer.OrdinalIgnoreCase);
+ resourceName = resourceCulture + "/" + resourceName;
+
+ if (!resourceData.satelliteResources.TryGetValue(resourceCulture, out resourceList))
+ {
+ resourceList = new ResourceHashesByNameDictionary();
+ resourceData.satelliteResources.Add(resourceCulture, resourceList);
+ }
+ }
+ else if (string.Equals(extension, ".pdb", StringComparison.OrdinalIgnoreCase))
+ {
+ resourceData.pdb ??= new ResourceHashesByNameDictionary();
+ resourceList = resourceData.pdb;
+ }
+ else if (string.Equals(extension, ".dll", StringComparison.OrdinalIgnoreCase))
+ {
+ resourceList = resourceData.assembly;
+ }
+ else if (string.Equals(assetType, "native", StringComparison.OrdinalIgnoreCase))
+ {
+ resourceList = resourceData.runtime;
+ }
+ else
+ {
+ // This should include items such as XML doc files, which do not need to be recorded in the manifest.
+ continue;
+ }
+
+ if (!resourceList.ContainsKey(resourceName))
+ {
+ resourceList.Add(resourceName, $"sha256-{resource.GetMetadata("FileHash")}");
+ }
+ }
+ }
+
+ if (ConfigurationFiles != null)
+ {
+ foreach (var configFile in ConfigurationFiles)
+ {
+ result.config.Add(Path.GetFileName(configFile.ItemSpec));
+ }
+ }
+
+ var serializer = new DataContractJsonSerializer(typeof(BootJsonData), new DataContractJsonSerializerSettings
+ {
+ UseSimpleDictionaryFormat = true
+ });
+
+ using var writer = JsonReaderWriterFactory.CreateJsonWriter(output, Encoding.UTF8, ownsStream: false, indent: true);
+ serializer.WriteObject(writer, result);
+ }
+
+ private bool IsLazyLoadedAssembly(string fileName)
+ {
+ return LazyLoadedAssemblies != null && LazyLoadedAssemblies.Any(a => a.ItemSpec == fileName);
+ }
+ }
+}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateServiceWorkerAssetsManifest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateServiceWorkerAssetsManifest.cs
new file mode 100644
index 0000000000..8e5f0d39e7
--- /dev/null
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateServiceWorkerAssetsManifest.cs
@@ -0,0 +1,97 @@
+// 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.IO;
+using System.Linq;
+using System.Runtime.Serialization.Json;
+using System.Security.Cryptography;
+using System.Text;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.AspNetCore.Razor.Tasks
+{
+ public partial class GenerateServiceWorkerAssetsManifest : Task
+ {
+ [Required]
+ public ITaskItem[] Assets { get; set; }
+
+ public string Version { get; set; }
+
+ [Required]
+ public string OutputPath { get; set; }
+
+ [Output]
+ public string CalculatedVersion { get; set; }
+
+ public override bool Execute()
+ {
+ using var fileStream = File.Create(OutputPath);
+ CalculatedVersion = GenerateAssetManifest(fileStream);
+
+ return true;
+ }
+
+ internal string GenerateAssetManifest(Stream stream)
+ {
+ var assets = new AssetsManifestFileEntry[Assets.Length];
+ System.Threading.Tasks.Parallel.For(0, assets.Length, i =>
+ {
+ var item = Assets[i];
+ var hash = item.GetMetadata("FileHash");
+ var url = item.GetMetadata("AssetUrl");
+
+ if (string.IsNullOrEmpty(hash))
+ {
+ // Some files that are part of the service worker manifest may not have their hashes previously
+ // calcualted. Calculate them at this time.
+ using var sha = SHA256.Create();
+ using var file = File.OpenRead(item.ItemSpec);
+ var bytes = sha.ComputeHash(file);
+
+ hash = Convert.ToBase64String(bytes);
+ }
+
+ assets[i] = new AssetsManifestFileEntry
+ {
+ hash = "sha256-" + hash,
+ url = url,
+ };
+ });
+
+ var version = Version;
+ if (string.IsNullOrEmpty(version))
+ {
+ // If a version isn't specified (which is likely the most common case), construct a Version by combining
+ // the file names + hashes of all the inputs.
+
+ var combinedHash = string.Join(
+ Environment.NewLine,
+ assets.OrderBy(f => f.url, StringComparer.Ordinal).Select(f => f.hash));
+
+ using var sha = SHA256.Create();
+ var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(combinedHash));
+ version = Convert.ToBase64String(bytes).Substring(0, 8);
+ }
+
+ var data = new AssetsManifestFile
+ {
+ version = version,
+ assets = assets,
+ };
+
+ using var streamWriter = new StreamWriter(stream, Encoding.UTF8, bufferSize: 50, leaveOpen: true);
+ streamWriter.Write("self.assetsManifest = ");
+ streamWriter.Flush();
+
+ using var jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8, ownsStream: false, indent: true);
+ new DataContractJsonSerializer(typeof(AssetsManifestFile)).WriteObject(jsonWriter, data);
+ jsonWriter.Flush();
+
+ streamWriter.WriteLine(";");
+
+ return version;
+ }
+ }
+}
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj b/src/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj
index efe499aaca..42cb964335 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj
@@ -34,12 +34,10 @@
-
diff --git a/src/Components/WebAssembly/Build/src/targets/Standalone.Web.config b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/BlazorWasm.web.config
similarity index 100%
rename from src/Components/WebAssembly/Build/src/targets/Standalone.Web.config
rename to src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/BlazorWasm.web.config
diff --git a/src/Components/WebAssembly/Build/src/targets/BuiltInBclLinkerDescriptor.xml b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/LinkerWorkaround.xml
similarity index 84%
rename from src/Components/WebAssembly/Build/src/targets/BuiltInBclLinkerDescriptor.xml
rename to src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/LinkerWorkaround.xml
index 6fb65f5a14..4461d2fc12 100644
--- a/src/Components/WebAssembly/Build/src/targets/BuiltInBclLinkerDescriptor.xml
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/LinkerWorkaround.xml
@@ -4,12 +4,6 @@
by the IL linker even if they are not referenced by user code. The file format is
described at https://github.com/mono/linker/blob/master/src/linker/README.md#syntax-of-xml-descriptor -->
-
-
-
-
-
@@ -19,6 +13,8 @@
+
+
<_RazorGenerateInputsHash>
<_RazorGenerateInputsHashFile>$(IntermediateOutputPath)$(MSBuildProjectName).RazorCoreGenerate.cache
-
- <_RazorToolAssembly Condition="'$(_RazorToolAssembly)'==''">$(RazorSdkDirectoryRoot)tools\netcoreapp3.0\rzc.dll
+
+
+
+
+
+
+ <_ServiceWorkerAssetsManifestIntermediateOutputPath>$(IntermediateOutputPath)$(ServiceWorkerAssetsManifest)
+ <_ServiceWorkerAssetsManifestFullPath>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)/$(_ServiceWorkerAssetsManifestIntermediateOutputPath)'))
+
+
+
+ <_ManifestStaticWebAsset Include="$(_ServiceWorkerAssetsManifestFullPath)">
+
+ $(PackageId)
+ $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\'))
+ $(StaticWebAssetBasePath)
+ $(ServiceWorkerAssetsManifest)
+ Never
+
+
+
+ <_ServiceWorkerIntermediateFile Include="@(ServiceWorker->'$(IntermediateOutputPath)serviceworkers\%(Identity)')">
+ %(ServiceWorker.PublishedContent)
+ %(ServiceWorker.Identity)
+ %(ServiceWorker.Identity)
+ %(ServiceWorker.Identity)
+ $([System.String]::Copy('%(ServiceWorker.Identity)').Substring(8))
+
+
+ <_ServiceWorkerStaticWebAsset Include="%(_ServiceWorkerIntermediateFile.FullPath)">
+
+ $(PackageId)
+ $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\'))
+ $(StaticWebAssetBasePath)
+ %(TargetOutputPath)
+ Never
+
+
+
+
+
+
+
+
+
+
+ <_ServiceWorkItem Include="@(StaticWebAsset)" Exclude="$(_ServiceWorkerAssetsManifestFullPath);@(_ServiceWorkerStaticWebAsset)">
+ $([System.String]::Copy('$([System.String]::Copy('%(StaticWebAsset.BasePath)').TrimEnd('/'))/%(StaticWebAsset.RelativePath)').Replace('\','/').TrimStart('/'))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ServiceWorkerAssetsManifestPublishIntermediateOutputPath>$(IntermediateOutputPath)publish-$(ServiceWorkerAssetsManifest)
+
+
+
+ <_ServiceWorkerIntermediatePublishFile Include="%(ServiceWorker.IntermediateOutputPath)serviceworkers\%(FileName).publish%(Extension)">
+ %(ServiceWorker.PublishedContent)
+ %(ServiceWorker.Identity)
+ %(ServiceWorker.Identity)
+
+
+ <_ServiceWorkerPublishFile Include="@(ResolvedFileToPublish)" Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))">
+ $([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').TrimStart('/'))
+ $([System.String]::Copy('%(RelativePath)').Replace('\','/').Substring(8))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Components.Wasm.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Components.Wasm.targets
new file mode 100644
index 0000000000..fe977ecdc0
--- /dev/null
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Components.Wasm.targets
@@ -0,0 +1,490 @@
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ true
+
+ /
+ true
+
+
+ false
+ false
+ false
+ false
+ false
+ exe
+
+
+ <_BlazorOutputPath>wwwroot\_framework\
+ <_BlazorSatelliteAssemblyCacheFile>$(IntermediateOutputPath)blazor.satelliteasm.props
+
+
+ $(OutputPath)$(PublishDirName)\
+
+
+
+
+
+
+
+
+
+ $(IntermediateOutputPath)linked\
+ $(IntermediateLinkDir)\
+
+ <_LinkSemaphore>$(IntermediateOutputPath)Link.semaphore
+
+
+
+
+
+
+
+ <_DotNetJsVersion>$(BundledNETCoreAppPackageVersion)
+ <_DotNetJsVersion Condition="'$(RuntimeFrameworkVersion)' != ''">$(RuntimeFrameworkVersion)
+ <_BlazorDotnetJsFileName>dotnet.$(_DotNetJsVersion).js
+ <_BlazorDotNetJsFilePath>$(IntermediateOutputPath)$(_BlazorDotnetJsFileName)
+
+
+
+ <_DotNetJsItem Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.DestinationSubPath)' == 'dotnet.js' AND '%(ReferenceCopyLocalPaths.AssetType)' == 'native'" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorJSFile Include="$(BlazorWebAssemblyJSPath)" />
+ <_BlazorJSFile Include="$(BlazorWebAssemblyJSMapPath)" Condition="Exists('$(BlazorWebAssemblyJSMapPath)')" />
+
+ <_BlazorConfigFile Include="wwwroot\appsettings*.json" />
+
+
+
+
+
+
+ <_BlazorCopyLocalPath
+ Include="@(ReferenceCopyLocalPaths)"
+ Exclude="@(ReferenceSatellitePaths)"/>
+
+ <_BlazorCopyLocalPath Include="@(IntermediateSatelliteAssembliesWithTargetPath)">
+ %(IntermediateSatelliteAssembliesWithTargetPath.Culture)\
+
+
+ <_BlazorOutputWithTargetPath Include="
+ @(_BlazorCopyLocalPath);
+ @(IntermediateAssembly);
+ @(_DebugSymbolsIntermediatePath);
+ @(_BlazorJSFile)" />
+
+ <_BlazorOutputWithTargetPath Include="@(ReferenceSatellitePaths)">
+ $([System.String]::Copy('%(ReferenceSatellitePaths.DestinationSubDirectory)').Trim('\').Trim('/'))
+
+
+
+
+
+
+
+
+
+
+ <_BlazorOutputWithTargetPath
+ Include="@(_BlazorReadSatelliteAssembly)"
+ Exclude="@(_BlazorOutputWithTargetPath)"
+ Condition="'@(_BlazorReadSatelliteAssembly->Count())' != '0'" />
+
+
+ <_BlazorOutputWithTargetPath
+ TargetPath="$(_BlazorOutputPath)%(_BlazorOutputWithTargetPath.DestinationSubDirectory)%(FileName)%(Extension)"
+ Condition="'%(__BlazorOutputWithTargetPath.TargetPath)' == ''" />
+
+
+
+
+
+ <_BlazorWriteSatelliteAssembly Include="@(_BlazorOutputWithTargetPath->HasMetadata('Culture'))" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ _BlazorWasmPrepareForRun;
+ $(PrepareForRunDependsOn)
+
+
+
+ $(GetCurrentProjectStaticWebAssetsDependsOn);
+ _BlazorWasmPrepareForRun;
+
+
+
+
+
+ <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json
+
+
+
+
+
+
+
+
+
+ <_BlazorWebAssemblyStaticWebAsset Include="$(_BlazorBuildBootJsonPath)">
+ $(PackageId)
+
+ $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\'))
+ $(StaticWebAssetBasePath)
+ _framework/blazor.boot.json
+ Never
+
+
+ <_BlazorWebAssemblyStaticWebAsset Include="@(_BlazorOutputWithHash)">
+ $(PackageId)
+
+ $([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\'))
+ $(StaticWebAssetBasePath)
+ $([System.String]::Copy('%(_BlazorOutputWithHash.TargetPath)').Replace('\','/').Substring(8))
+ Never
+
+
+
+ <_ExternalStaticWebAsset Include="@(_BlazorWebAssemblyStaticWebAsset)" SourceType="Generated" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorApplicationTrimmerDescriptorFile>$(IntermediateOutputPath)default.trimmerdescriptor.xml
+ <_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml
+
+
+
+ <_BlazorTypeGranularAssembly
+ Include="@(ResolvedFileToPublish)"
+ Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.'))">
+ false
+ all
+
+
+ <_BlazorApplicationAssembly Include="@(IntermediateAssembly)">
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorPublishBootResource
+ Include="@(ResolvedFileToPublish)"
+ Condition="$([System.String]::Copy('%(RelativePath)').Replace('\','/').StartsWith('wwwroot/_framework')) AND '%(Extension)' != '.a'" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_CompressedFileOutputPath>$(IntermediateOutputPath)brotli\
+ <_BlazorWebAssemblyBrotliIncremental>true
+
+
+
+ <_BrotliFileToCompress
+ Include="@(ResolvedFileToPublish)"
+ Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_PublishingBlazorWasmProject>true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %(RelativePath)
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets
index 9a66b1b926..71721b675d 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets
@@ -269,7 +269,7 @@ Copyright (c) .NET Foundation. All rights reserved.
+ KeepMetadata="ContentRoot;BasePath;RelativePath;SourceId;SourceType;CopyToPublishDirectory" />
@@ -291,7 +291,7 @@ Copyright (c) .NET Foundation. All rights reserved.
-
+
@@ -513,7 +513,7 @@ Copyright (c) .NET Foundation. All rights reserved.
-->
<_ExternalPublishStaticWebAsset
Include="%(StaticWebAsset.FullPath)"
- Condition="'%(SourceType)' != ''">
+ Condition="'%(SourceType)' != '' AND '%(StaticWebAsset.CopyToPublishDirectory)' != 'Never'">
PreserveNewest
$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)','$([MSBuild]::NormalizePath('wwwroot\%(BasePath)\%(RelativePath)'))'))
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.props b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.props
index 6fad4d5180..7e49bc4277 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.props
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.props
@@ -14,4 +14,5 @@ Copyright (c) .NET Foundation. All rights reserved.
$(MSBuildThisFileDirectory)Sdk.Razor.CurrentVersion.props
$(MSBuildThisFileDirectory)Sdk.Razor.CurrentVersion.targets
-
+
+
\ No newline at end of file
diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props
index b7d17b206b..19e380c3a4 100644
--- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props
+++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.props
@@ -18,6 +18,8 @@ Copyright (c) .NET Foundation. All rights reserved.
Default properties for common Razor SDK behavior.
-->
+ true
+
<_TargetFrameworkVersionWithoutV>$(TargetFrameworkVersion.TrimStart('vV'))
<_TargetingNETCoreApp30OrLater Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' >= '3.0'">true
+ <_TargetingNET50OrLater Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' >= '5.0'">true
OutOfProcess
+
+
+ <_BlazorBrotliCompressionLevel>NoCompression
@@ -37,7 +41,6 @@
<_MvcAssemblyName Include="Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib" />
-
+
+
+
diff --git a/src/Razor/test/testassets/SimpleMvc21/SimpleMvc21.csproj b/src/Razor/test/testassets/SimpleMvc21/SimpleMvc21.csproj
index 2f53950aa6..292a0a83df 100644
--- a/src/Razor/test/testassets/SimpleMvc21/SimpleMvc21.csproj
+++ b/src/Razor/test/testassets/SimpleMvc21/SimpleMvc21.csproj
@@ -7,6 +7,7 @@
netcoreapp2.1
+ $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\
diff --git a/src/Razor/test/testassets/blazor.webassembly.js b/src/Razor/test/testassets/blazor.webassembly.js
new file mode 100644
index 0000000000..84362ca046
--- /dev/null
+++ b/src/Razor/test/testassets/blazor.webassembly.js
@@ -0,0 +1 @@
+Test file
\ No newline at end of file
diff --git a/src/Razor/test/testassets/blazorhosted-rid/Program.cs b/src/Razor/test/testassets/blazorhosted-rid/Program.cs
new file mode 100644
index 0000000000..cb0046ec11
--- /dev/null
+++ b/src/Razor/test/testassets/blazorhosted-rid/Program.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace blazorhosted.Server
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ Console.WriteLine(typeof(string));
+ }
+ }
+}
diff --git a/src/Razor/test/testassets/blazorhosted-rid/blazorhosted-rid.csproj b/src/Razor/test/testassets/blazorhosted-rid/blazorhosted-rid.csproj
new file mode 100644
index 0000000000..1e14923fb3
--- /dev/null
+++ b/src/Razor/test/testassets/blazorhosted-rid/blazorhosted-rid.csproj
@@ -0,0 +1,22 @@
+
+
+
+ $(DefaultNetCoreTargetFramework)
+ $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\
+
+ linux-x64
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Components/WebAssembly/Build/testassets/blazorhosted/Program.cs b/src/Razor/test/testassets/blazorhosted/Program.cs
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/blazorhosted/Program.cs
rename to src/Razor/test/testassets/blazorhosted/Program.cs
diff --git a/src/Razor/test/testassets/blazorhosted/blazorhosted.csproj b/src/Razor/test/testassets/blazorhosted/blazorhosted.csproj
new file mode 100644
index 0000000000..28c8a47ce5
--- /dev/null
+++ b/src/Razor/test/testassets/blazorhosted/blazorhosted.csproj
@@ -0,0 +1,12 @@
+
+
+
+ $(DefaultNetCoreTargetFramework)
+ $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\
+
+
+
+
+
+
+
diff --git a/src/Components/WebAssembly/Build/testassets/standalone/App.razor b/src/Razor/test/testassets/blazorwasm/App.razor
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/standalone/App.razor
rename to src/Razor/test/testassets/blazorwasm/App.razor
diff --git a/src/Components/WebAssembly/Build/testassets/standalone/LinkToWebRoot/css/app.css b/src/Razor/test/testassets/blazorwasm/LinkToWebRoot/css/app.css
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/standalone/LinkToWebRoot/css/app.css
rename to src/Razor/test/testassets/blazorwasm/LinkToWebRoot/css/app.css
diff --git a/src/Components/WebAssembly/Build/testassets/standalone/Pages/Index.razor b/src/Razor/test/testassets/blazorwasm/Pages/Index.razor
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/standalone/Pages/Index.razor
rename to src/Razor/test/testassets/blazorwasm/Pages/Index.razor
diff --git a/src/Components/WebAssembly/Build/testassets/standalone/Program.cs b/src/Razor/test/testassets/blazorwasm/Program.cs
similarity index 83%
rename from src/Components/WebAssembly/Build/testassets/standalone/Program.cs
rename to src/Razor/test/testassets/blazorwasm/Program.cs
index 7d2cb4eeea..4b20dea9de 100644
--- a/src/Components/WebAssembly/Build/testassets/standalone/Program.cs
+++ b/src/Razor/test/testassets/blazorwasm/Program.cs
@@ -6,6 +6,7 @@ namespace standalone
{
public static void Main(string[] args)
{
+ GC.KeepAlive(typeof(System.Text.Json.JsonSerializer));
GC.KeepAlive(typeof(RazorClassLibrary.Class1));
#if REFERENCE_classlibrarywithsatelliteassemblies
GC.KeepAlive(typeof(classlibrarywithsatelliteassemblies.Class1));
diff --git a/src/Components/WebAssembly/Build/testassets/standalone/Resources.ja.resx.txt b/src/Razor/test/testassets/blazorwasm/Resources.ja.resx.txt
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/standalone/Resources.ja.resx.txt
rename to src/Razor/test/testassets/blazorwasm/Resources.ja.resx.txt
diff --git a/src/Components/WebAssembly/Build/testassets/standalone/_Imports.razor b/src/Razor/test/testassets/blazorwasm/_Imports.razor
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/standalone/_Imports.razor
rename to src/Razor/test/testassets/blazorwasm/_Imports.razor
diff --git a/src/Razor/test/testassets/blazorwasm/blazorwasm.csproj b/src/Razor/test/testassets/blazorwasm/blazorwasm.csproj
new file mode 100644
index 0000000000..724a4365b5
--- /dev/null
+++ b/src/Razor/test/testassets/blazorwasm/blazorwasm.csproj
@@ -0,0 +1,45 @@
+
+
+ net5.0
+ true
+ browser-wasm
+ false
+ $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\
+ custom-service-worker-assets.js
+
+
+
+
+
+
+ false
+
+
+
+
+ $(RepoRoot)artifacts\bin\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib\$(Configuration)\netstandard2.0\
+
+
+
+
+
+
+
+
+
+
+
+ wwwroot\
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Components/WebAssembly/Build/testassets/standalone/wwwroot/Fake-License.txt b/src/Razor/test/testassets/blazorwasm/wwwroot/Fake-License.txt
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/standalone/wwwroot/Fake-License.txt
rename to src/Razor/test/testassets/blazorwasm/wwwroot/Fake-License.txt
diff --git a/src/Components/WebAssembly/Build/testassets/standalone/wwwroot/css/app.css b/src/Razor/test/testassets/blazorwasm/wwwroot/css/app.css
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/standalone/wwwroot/css/app.css
rename to src/Razor/test/testassets/blazorwasm/wwwroot/css/app.css
diff --git a/src/Components/WebAssembly/Build/testassets/standalone/wwwroot/index.html b/src/Razor/test/testassets/blazorwasm/wwwroot/index.html
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/standalone/wwwroot/index.html
rename to src/Razor/test/testassets/blazorwasm/wwwroot/index.html
diff --git a/src/Components/WebAssembly/Build/testassets/standalone/wwwroot/serviceworkers/my-prod-service-worker.js b/src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/standalone/wwwroot/serviceworkers/my-prod-service-worker.js
rename to src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js
diff --git a/src/Components/WebAssembly/Build/testassets/standalone/wwwroot/serviceworkers/my-service-worker.js b/src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/standalone/wwwroot/serviceworkers/my-service-worker.js
rename to src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js
diff --git a/src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/Class1.cs b/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Class1.cs
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/Class1.cs
rename to src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Class1.cs
diff --git a/src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx b/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx
rename to src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx
diff --git a/src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj b/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj
similarity index 77%
rename from src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj
rename to src/Razor/test/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj
index 7081842748..f1a186fb94 100644
--- a/src/Components/WebAssembly/Build/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj
+++ b/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj
@@ -3,6 +3,7 @@
netstandard2.1
3.0
+ $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\
diff --git a/src/Components/WebAssembly/Build/testassets/razorclasslibrary/Class1.cs b/src/Razor/test/testassets/razorclasslibrary/Class1.cs
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/razorclasslibrary/Class1.cs
rename to src/Razor/test/testassets/razorclasslibrary/Class1.cs
diff --git a/src/Components/WebAssembly/Build/testassets/razorclasslibrary/RazorClassLibrary.csproj b/src/Razor/test/testassets/razorclasslibrary/RazorClassLibrary.csproj
similarity index 63%
rename from src/Components/WebAssembly/Build/testassets/razorclasslibrary/RazorClassLibrary.csproj
rename to src/Razor/test/testassets/razorclasslibrary/RazorClassLibrary.csproj
index 94e866815d..9480a0542e 100644
--- a/src/Components/WebAssembly/Build/testassets/razorclasslibrary/RazorClassLibrary.csproj
+++ b/src/Razor/test/testassets/razorclasslibrary/RazorClassLibrary.csproj
@@ -3,6 +3,7 @@
netstandard2.1
3.0
+ $(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\
diff --git a/src/Components/WebAssembly/Build/testassets/razorclasslibrary/wwwroot/styles.css b/src/Razor/test/testassets/razorclasslibrary/wwwroot/styles.css
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/razorclasslibrary/wwwroot/styles.css
rename to src/Razor/test/testassets/razorclasslibrary/wwwroot/styles.css
diff --git a/src/Components/WebAssembly/Build/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js b/src/Razor/test/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js
similarity index 100%
rename from src/Components/WebAssembly/Build/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js
rename to src/Razor/test/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js