--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using System.Threading.Tasks;
+using Xunit;
+using Xunit.Abstractions;
+
+#nullable enable
+
+namespace Wasm.Build.Tests.Blazor;
+
+public class AppsettingsTests : BuildTestBase
+{
+ public AppsettingsTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ _enablePerTestCleanup = true;
+ }
+
+ [Fact]
+ public async Task FileInVfs()
+ {
+ string id = $"blazor_{Path.GetRandomFileName()}";
+ string projectFile = CreateWasmTemplateProject(id, "blazorwasm");
+
+ string projectDirectory = Path.GetDirectoryName(projectFile)!;
+
+ File.WriteAllText(Path.Combine(projectDirectory, "wwwroot", "appsettings.json"), $"{{ \"Id\": \"{id}\" }}");
+
+ string programPath = Path.Combine(projectDirectory, "Program.cs");
+ string programContent = File.ReadAllText(programPath);
+ programContent = programContent.Replace("var builder",
+ """
+ System.Console.WriteLine($"appSettings Exists '{File.Exists("/appsettings.json")}'");
+ System.Console.WriteLine($"appSettings Content '{File.ReadAllText("/appsettings.json")}'");
+ var builder
+ """);
+ File.WriteAllText(programPath, programContent);
+
+ BlazorBuild(new BlazorBuildOptions(id, "debug", NativeFilesType.FromRuntimePack));
+
+ bool existsChecked = false;
+ bool contentChecked = false;
+
+ await BlazorRunForBuildWithDotnetRun("debug", onConsoleMessage: msg =>
+ {
+ if (msg.Text.Contains("appSettings Exists 'True'"))
+ existsChecked = true;
+ else if (msg.Text.Contains($"appSettings Content '{{ \"Id\": \"{id}\" }}'"))
+ contentChecked = true;
+ });
+
+ Assert.True(existsChecked, "File '/appsettings.json' wasn't found");
+ Assert.True(contentChecked, "Content of '/appsettings.json' is not matched");
+ }
+}
\ No newline at end of file
same: dotnetWasmFromRuntimePack);
}
- protected void AssertBlazorBootJson(string config, bool isPublish, bool isNet7AndBelow, string targetFramework = DefaultTargetFrameworkForBlazor, string? binFrameworkDir=null)
+ protected void AssertBlazorBootJson(string config, bool isPublish, bool isNet7AndBelow, string targetFramework = DefaultTargetFrameworkForBlazor, string? binFrameworkDir = null)
{
binFrameworkDir ??= FindBlazorBinFrameworkDir(config, isPublish, targetFramework);
// Keeping these methods with explicit Build/Publish in the name
// so in the test code it is evident which is being run!
- public Task BlazorRunForBuildWithDotnetRun(string config, Func<IPage, Task>? test = null, string extraArgs = "--no-build")
- => BlazorRunTest($"run -c {config} {extraArgs}", _projectDir!, test);
+ public Task BlazorRunForBuildWithDotnetRun(string config, Func<IPage, Task>? test = null, string extraArgs = "--no-build", Action<IConsoleMessage>? onConsoleMessage = null)
+ => BlazorRunTest($"run -c {config} {extraArgs}", _projectDir!, test, onConsoleMessage);
- public Task BlazorRunForPublishWithWebServer(string config, Func<IPage, Task>? test = null, string extraArgs = "")
+ public Task BlazorRunForPublishWithWebServer(string config, Func<IPage, Task>? test = null, string extraArgs = "", Action<IConsoleMessage>? onConsoleMessage = null)
=> BlazorRunTest($"{s_xharnessRunnerCommand} wasm webserver --app=. --web-server-use-default-files {extraArgs}",
Path.GetFullPath(Path.Combine(FindBlazorBinFrameworkDir(config, forPublish: true), "..")),
- test);
+ test, onConsoleMessage);
public async Task BlazorRunTest(string runArgs,
string workingDirectory,
Func<IPage, Task>? test = null,
+ Action<IConsoleMessage>? onConsoleMessage = null,
bool detectRuntimeFailures = true)
{
using var runCommand = new RunCommand(s_buildEnv, _testOutput)
Console.WriteLine($"[{msg.Type}] {msg.Text}");
_testOutput.WriteLine($"[{msg.Type}] {msg.Text}");
+ onConsoleMessage?.Invoke(msg);
+
if (detectRuntimeFailures)
{
if (msg.Text.Contains("[MONO] * Assertion") || msg.Text.Contains("Error: [MONO] "))
import type { WebAssemblyBootResourceType } from "../../types";
import { loaderHelpers } from "../globals";
-type LoadBootResourceCallback = (type: WebAssemblyBootResourceType, name: string, defaultUri: string, integrity: string) => string | Promise<Response> | null | undefined;
+export type LoadBootResourceCallback = (type: WebAssemblyBootResourceType, name: string, defaultUri: string, integrity: string) => string | Promise<Response> | null | undefined;
export class BootConfigResult {
private constructor(public bootConfig: BootJsonData, public applicationEnvironment: string) {
import { toAbsoluteUri } from "./_Polyfill";
const networkFetchCacheMode = "no-cache";
+const cacheSkipResourceTypes = ["configuration"];
+
export class WebAssemblyResourceLoader {
private usedCacheKeys: { [key: string]: boolean } = {};
}
loadResource(name: string, url: string, contentHash: string, resourceType: WebAssemblyBootResourceType): LoadingResource {
- const response = this.cacheIfUsed
+ const response = this.cacheIfUsed && !cacheSkipResourceTypes.includes(resourceType)
? this.loadResourceWithCaching(this.cacheIfUsed, name, url, contentHash, resourceType)
: this.loadResourceWithoutCaching(name, url, contentHash, resourceType);
// Note that if cacheBootResources was explicitly disabled, we also bypass hash checking
// This is to give developers an easy opt-out from the entire caching/validation flow if
// there's anything they don't like about it.
- return fetch(url, {
- cache: networkFetchCacheMode,
- integrity: this.bootConfig.cacheBootResources ? contentHash : undefined,
- });
+ const fetchOptions: RequestInit = {
+ cache: networkFetchCacheMode
+ };
+
+ if (resourceType === "configuration") {
+ // Include credentials so the server can allow download / provide user specific file
+ fetchOptions.credentials = "include";
+ } else {
+ // Any other resource than configuration should provide integrity check
+ fetchOptions.integrity = this.bootConfig.cacheBootResources ? contentHash : undefined;
+ }
+
+ return fetch(url, fetchOptions);
}
private async addToCacheAsync(cache: Cache, name: string, cacheKey: string, response: Response) {
}
export async function initializeBootConfig(bootConfigResult: BootConfigResult, module: DotnetModuleInternal, startupOptions?: Partial<WebAssemblyStartOptions>) {
- INTERNAL.resourceLoader = resourceLoader = await WebAssemblyResourceLoader.initAsync(bootConfigResult.bootConfig, startupOptions || {});
+ INTERNAL.resourceLoader = resourceLoader = await WebAssemblyResourceLoader.initAsync(bootConfigResult.bootConfig, startupOptions ?? {});
mapBootConfigToMonoConfig(loaderHelpers.config, bootConfigResult.applicationEnvironment);
setupModuleForBlazor(module);
}
let totalResources = 0;
const behaviorByName = (name: string): AssetBehaviours | "other" => {
- return name === "dotnet.timezones.blat" ? "vfs"
- : name === "dotnet.native.wasm" ? "dotnetwasm"
- : (name.startsWith("dotnet.native.worker") && name.endsWith(".js")) ? "js-module-threads"
- : (name.startsWith("dotnet.native") && name.endsWith(".js")) ? "js-module-native"
- : (name.startsWith("dotnet.runtime") && name.endsWith(".js")) ? "js-module-runtime"
- : (name.startsWith("dotnet") && name.endsWith(".js")) ? "js-module-dotnet"
- : name.startsWith("icudt") ? "icu"
- : "other";
+ return name === "dotnet.native.wasm" ? "dotnetwasm"
+ : (name.startsWith("dotnet.native.worker") && name.endsWith(".js")) ? "js-module-threads"
+ : (name.startsWith("dotnet.native") && name.endsWith(".js")) ? "js-module-native"
+ : (name.startsWith("dotnet.runtime") && name.endsWith(".js")) ? "js-module-runtime"
+ : (name.startsWith("dotnet") && name.endsWith(".js")) ? "js-module-dotnet"
+ : name.startsWith("icudt") ? "icu"
+ : "other";
};
const monoToBlazorAssetTypeMap: { [key: string]: WebAssemblyBootResourceType | undefined } = {
"assembly": "assembly",
"pdb": "pdb",
"icu": "globalization",
- "vfs": "globalization",
+ "vfs": "configuration",
"dotnetwasm": "dotnetwasm",
};
};
assets.push(asset);
}
+ for (let i = 0; i < resourceLoader.bootConfig.config.length; i++) {
+ const config = resourceLoader.bootConfig.config[i];
+ if (config === "appsettings.json" || config === `appsettings.${applicationEnvironment}.json`) {
+ assets.push({
+ name: config,
+ resolvedUrl: config,
+ behavior: "vfs",
+ });
+ }
+ }
if (!hasIcuData) {
moduleConfig.globalizationMode = "invariant";
All = 1,
Invariant = 2,
Custom = 3
-}
+}
\ No newline at end of file