From 0e1881a8208fceb4bcad090340bc8d2b9ba151c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Sep 2025 11:29:27 +0000 Subject: [PATCH 1/6] URL encode PackageId in scoped CSS asset paths to fix non-ASCII project names Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com> --- ....NET.Sdk.StaticWebAssets.ScopedCss.targets | 10 +++---- .../ScopedCssIntegrationTests.cs | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets index e74f0589b497..6a415883af2f 100644 --- a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets +++ b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets @@ -233,18 +233,18 @@ Integration with static web assets: <_ScopedCssAppBundleStaticWebAssetCandidate Include="$(_ScopedCssOutputPath)"> - $(PackageId)#[.{fingerprint}]?.styles.css - $(PackageId).styles.css + $([System.Uri]::EscapeDataString($(PackageId)))#[.{fingerprint}]?.styles.css + $([System.Uri]::EscapeDataString($(PackageId))).styles.css <_ScopedCssProjectBundleStaticWebAssetCandidate Include="$(_ScopedCssProjectOutputPath)"> - $(PackageId)#[.{fingerprint}]!.bundle.scp.css - $(PackageId).bundle.scp.css + $([System.Uri]::EscapeDataString($(PackageId)))#[.{fingerprint}]!.bundle.scp.css + $([System.Uri]::EscapeDataString($(PackageId))).bundle.scp.css <_AddLinkHeaderToAppBundle Include="Append" Condition="'@(_ResolvedScopedCssBundleEndpoints)' != ''"> Header Link - @(_ResolvedScopedCssBundleEndpoints->'<%(Identity)>; rel="preload"; as="style"', ', ') + @(_ResolvedScopedCssBundleEndpoints->'<$([System.Uri]::EscapeDataString(%(Identity)))>; rel="preload"; as="style"', ', ') diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs index 9668d8656913..c6a7fd8830ed 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs @@ -617,5 +617,35 @@ public void RegeneratingScopedCss_ForProjectWithReferences() text.Should().Contain("background-color: orangered"); text.Should().MatchRegex(""".*@import '_content/ClassLibrary/ClassLibrary\.[a-zA-Z0-9]+\.bundle\.scp\.css.*"""); } + + [Fact] + public void Build_GeneratesUrlEncodedLinkHeaderForNonAsciiProjectName() + { + var testAsset = "RazorComponentApp"; + var projectDirectory = CreateAspNetSdkTestAsset(testAsset); + + // Create a CSS file to trigger scoped CSS processing + var cssFile = Path.Combine(projectDirectory.Path, "Components", "Pages", "Index.razor.css"); + Directory.CreateDirectory(Path.GetDirectoryName(cssFile)); + File.WriteAllText(cssFile, ".test { color: red; }"); + + var build = CreateBuildCommand(projectDirectory); + // Set PackageId to contain non-ASCII characters (Chinese characters meaning "project") + ExecuteCommand(build, "/p:PackageId=项目").Should().Pass(); + + var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString(); + + // Check that the staticwebassets.build.endpoints.json file contains URL-encoded characters + var endpointsFile = Path.Combine(intermediateOutputPath, "staticwebassets.build.endpoints.json"); + new FileInfo(endpointsFile).Should().Exist(); + + var endpointsContent = File.ReadAllText(endpointsFile); + + // Verify that the Link header contains URL-encoded characters (%E9%A1%B9%E7%9B%AE is "项目" encoded) + endpointsContent.Should().Contain("%E9%A1%B9%E7%9B%AE"); + + // Verify it doesn't contain the unencoded characters + endpointsContent.Should().NotContain("项目"); + } } } From dec378b3db1e2b7d37cfe1ed3a3a47ca5262c191 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Sep 2025 11:32:16 +0000 Subject: [PATCH 2/6] Final verification that URL encoding fix works correctly Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com> --- .../ComputeStaticWebAssetsTargetPathsTest.cs | 1 + .../JsModulesIntegrationTest.cs | 14 +++--- .../LegacyStaticWebAssetsV1IntegrationTest.cs | 2 +- .../ScopedCssIntegrationTests.cs | 8 ++-- .../StaticWebAssetEndpointsIntegrationTest.cs | 48 +++++++++---------- .../DefineStaticWebAssetEndpointsTest.cs | 6 +-- .../DiscoverPrecompressedAssetsTest.cs | 2 +- .../DiscoverStaticWebAssetsTest.cs | 8 ++-- .../FilterStaticWebAssetEndpointsTest.cs | 1 + ...rateStaticWebAssetEndpointsManifestTest.cs | 6 +-- ...ateStaticWebAssetEndpointsPropsFileTest.cs | 2 +- .../Globbing/StaticWebAssetGlobMatcherTest.cs | 46 +++++++++--------- .../OverrideHtmlAssetPlaceholdersTest.cs | 2 +- .../ResolveCompressedAssetsTest.cs | 4 +- ...tedStaticWebAssetEndpointsForAssetsTest.cs | 2 +- ...ateExternallyDefinedStaticWebAssetsTest.cs | 2 +- .../UpdateStaticWebAssetEndpointsTest.cs | 2 +- .../StaticWebAssetsBaselineFactory.cs | 1 + ...aticWebAssetsCompressionIntegrationTest.cs | 2 +- .../StaticWebAssetsFingerprintingTest.cs | 7 +-- .../StaticWebAssetsIntegrationTest.cs | 2 +- 21 files changed, 87 insertions(+), 81 deletions(-) diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ComputeStaticWebAssetsTargetPathsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ComputeStaticWebAssetsTargetPathsTest.cs index 8bf1bafb89dd..114a097d7c63 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ComputeStaticWebAssetsTargetPathsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ComputeStaticWebAssetsTargetPathsTest.cs @@ -13,6 +13,7 @@ using Moq; namespace Microsoft.NET.Sdk.StaticWebAssets.Tests; + public class ComputeStaticWebAssetsTargetPathsTest { [Fact] diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/JsModulesIntegrationTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/JsModulesIntegrationTest.cs index d96dab0d59e5..56ad90dfc8ed 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/JsModulesIntegrationTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/JsModulesIntegrationTest.cs @@ -31,11 +31,12 @@ public void Build_GeneratesManifestWhenItFindsALibrary() { var testAsset = "RazorComponentApp"; var projectDirectory = CreateAspNetSdkTestAsset(testAsset) - .WithProjectChanges(p => { - var fingerprintContent = p.Descendants() - .SingleOrDefault(e => e.Name.LocalName == "StaticWebAssetsFingerprintContent"); - fingerprintContent.Value = "true"; - }); + .WithProjectChanges(p => + { + var fingerprintContent = p.Descendants() + .SingleOrDefault(e => e.Name.LocalName == "StaticWebAssetsFingerprintContent"); + fingerprintContent.Value = "true"; + }); Directory.CreateDirectory(Path.Combine(projectDirectory.TestRoot, "wwwroot")); File.WriteAllText(Path.Combine(projectDirectory.TestRoot, "wwwroot", "ComponentApp.lib.module.js"), "console.log('Hello world!');"); @@ -89,7 +90,8 @@ public void Publish_PublishesJsModuleBundleBundleToTheRightLocation() { var testAsset = "RazorComponentApp"; ProjectDirectory = CreateAspNetSdkTestAsset(testAsset) - .WithProjectChanges(p => { + .WithProjectChanges(p => + { var fingerprintContent = p.Descendants() .SingleOrDefault(e => e.Name.LocalName == "StaticWebAssetsFingerprintContent"); fingerprintContent.Value = "true"; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/LegacyStaticWebAssetsV1IntegrationTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/LegacyStaticWebAssetsV1IntegrationTest.cs index 9089ed148d04..248b025ac4a5 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/LegacyStaticWebAssetsV1IntegrationTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/LegacyStaticWebAssetsV1IntegrationTest.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.StaticWebAssets.Tasks; -[assembly:CollectionBehavior(DisableTestParallelization = true)] +[assembly: CollectionBehavior(DisableTestParallelization = true)] namespace Microsoft.NET.Sdk.StaticWebAssets.Tests { diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs index c6a7fd8830ed..245976d4cabe 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs @@ -634,16 +634,16 @@ public void Build_GeneratesUrlEncodedLinkHeaderForNonAsciiProjectName() ExecuteCommand(build, "/p:PackageId=项目").Should().Pass(); var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString(); - + // Check that the staticwebassets.build.endpoints.json file contains URL-encoded characters var endpointsFile = Path.Combine(intermediateOutputPath, "staticwebassets.build.endpoints.json"); new FileInfo(endpointsFile).Should().Exist(); - + var endpointsContent = File.ReadAllText(endpointsFile); - + // Verify that the Link header contains URL-encoded characters (%E9%A1%B9%E7%9B%AE is "项目" encoded) endpointsContent.Should().Contain("%E9%A1%B9%E7%9B%AE"); - + // Verify it doesn't contain the unencoded characters endpointsContent.Should().NotContain("项目"); } diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetEndpointsIntegrationTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetEndpointsIntegrationTest.cs index 79442747cab9..ade219c5f8f4 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetEndpointsIntegrationTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetEndpointsIntegrationTest.cs @@ -85,9 +85,9 @@ private bool MatchUncompresedProjectBundlesNoFingerprint(StaticWebAssetEndpoint Success: true, Groups: [ var _, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: "", Success: false }, - { Name: "compress", Value: "", Success: false } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: "", Success: false }, + { Name: "compress", Value: "", Success: false } ] }; @@ -96,9 +96,9 @@ private bool MatchCompressedProjectBundlesNoFingerprint(StaticWebAssetEndpoint e Success: true, Groups: [ var _, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: "", Success: false }, - { Name: "compress", Value: var compress, Success: true } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: "", Success: false }, + { Name: "compress", Value: var compress, Success: true } ] } && (compress == ".gz" || compress == ".br"); @@ -107,9 +107,9 @@ private bool MatchUncompressedProjectBundlesWithFingerprint(StaticWebAssetEndpoi Success: true, Groups: [ var m, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: var fingerprint, Success: true }, - { Name: "compress", Value: "", Success: false } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: var fingerprint, Success: true }, + { Name: "compress", Value: "", Success: false } ] } && fingerprint == ep.EndpointProperties.Single(p => p.Name == "fingerprint").Value; @@ -118,9 +118,9 @@ private bool MatchCompressedProjectBundlesWithFingerprint(StaticWebAssetEndpoint Success: true, Groups: [ var m, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: var fingerprint, Success: true }, - { Name: "compress", Value: var compress, Success: true } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: var fingerprint, Success: true }, + { Name: "compress", Value: var compress, Success: true } ] } && !string.IsNullOrWhiteSpace(fingerprint) && (compress == ".gz" || compress == ".br"); @@ -130,9 +130,9 @@ private bool MatchUncompressedAppBundleNoFingerprint(StaticWebAssetEndpoint ep) Success: true, Groups: [ var _, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: "", Success: false }, - { Name: "compress", Value: "", Success: false } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: "", Success: false }, + { Name: "compress", Value: "", Success: false } ] }; @@ -141,9 +141,9 @@ private bool MatchCompressedAppBundleNoFingerprint(StaticWebAssetEndpoint ep) => Success: true, Groups: [ var _, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: "", Success: false }, - { Name: "compress", Value: var compress, Success: true } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: "", Success: false }, + { Name: "compress", Value: var compress, Success: true } ] } && (compress == ".gz" || compress == ".br"); @@ -152,9 +152,9 @@ private bool MatchUncompressedAppBundleWithFingerprint(StaticWebAssetEndpoint ep Success: true, Groups: [ var m, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: var fingerprint, Success: true }, - { Name: "compress", Value: "", Success: false } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: var fingerprint, Success: true }, + { Name: "compress", Value: "", Success: false } ] } && fingerprint == ep.EndpointProperties.Single(p => p.Name == "fingerprint").Value; @@ -163,9 +163,9 @@ private bool MatchCompressedAppBundleWithFingerprint(StaticWebAssetEndpoint ep) Success: true, Groups: [ var m, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: var fingerprint, Success: true }, - { Name: "compress", Value: var compress, Success: true } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: var fingerprint, Success: true }, + { Name: "compress", Value: var compress, Success: true } ] } && !string.IsNullOrWhiteSpace(fingerprint) && (compress == ".gz" || compress == ".br"); diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DefineStaticWebAssetEndpointsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DefineStaticWebAssetEndpointsTest.cs index dfb709b5865d..ccab6a4fe566 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DefineStaticWebAssetEndpointsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DefineStaticWebAssetEndpointsTest.cs @@ -3,15 +3,15 @@ #nullable disable -using System.Diagnostics.Metrics; using System.Diagnostics; +using System.Diagnostics.Metrics; +using System.Globalization; +using System.Net; using Microsoft.AspNetCore.StaticWebAssets.Tasks; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Moq; using NuGet.Packaging.Core; -using System.Net; -using System.Globalization; namespace Microsoft.NET.Sdk.StaticWebAssets.Tests; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverPrecompressedAssetsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverPrecompressedAssetsTest.cs index 27d7baae60f0..5eccf523e3e7 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverPrecompressedAssetsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverPrecompressedAssetsTest.cs @@ -44,7 +44,7 @@ public void DiscoversPrecompressedAssetsCorrectly() CopyToOutputDirectory = StaticWebAsset.AssetCopyOptions.Never, Fingerprint = "uncompressed", RelatedAsset = string.Empty, - ContentRoot = Path.Combine(Environment.CurrentDirectory,"wwwroot"), + ContentRoot = Path.Combine(Environment.CurrentDirectory, "wwwroot"), SourceType = StaticWebAsset.SourceTypes.Discovered, Integrity = "uncompressed-integrity", AssetRole = StaticWebAsset.AssetRoles.Primary, diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs index f252022d1338..e56d53b8b7d2 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs @@ -217,9 +217,9 @@ public void FingerprintsContentUsingPatternsWhenMoreThanOneExtension(string file asset.GetMetadata(nameof(StaticWebAsset.OriginalItemSpec)).Should().Be(Path.Combine("wwwroot", fileName)); } - [Fact] - [Trait("Category", "FingerprintIdentity")] - public void ComputesIdentity_UsingFingerprintPattern_ForComputedAssets_WhenIdentityNeedsComputation() + [Fact] + [Trait("Category", "FingerprintIdentity")] + public void ComputesIdentity_UsingFingerprintPattern_ForComputedAssets_WhenIdentityNeedsComputation() { // Arrange: simulate a packaged asset (outside content root) with a RelativePath inside the app var errorMessages = new List(); @@ -251,7 +251,7 @@ public void ComputesIdentity_UsingFingerprintPattern_ForComputedAssets_WhenIdent }) ], // No RelativePathPattern, we trigger the branch that synthesizes identity under content root. - FingerprintPatterns = [ new TaskItem("Js", new Dictionary{{"Pattern","*.js"},{"Expression","#[.{fingerprint}]!"}})], + FingerprintPatterns = [new TaskItem("Js", new Dictionary { { "Pattern", "*.js" }, { "Expression", "#[.{fingerprint}]!" } })], FingerprintCandidates = true, SourceType = "Computed", SourceId = "Client", diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/FilterStaticWebAssetEndpointsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/FilterStaticWebAssetEndpointsTest.cs index 2cf9ee746d4e..7b8a19394706 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/FilterStaticWebAssetEndpointsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/FilterStaticWebAssetEndpointsTest.cs @@ -9,6 +9,7 @@ using Moq; namespace Microsoft.NET.Sdk.StaticWebAssets.Tests.StaticWebAssets; + public class FilterStaticWebAssetEndpointsTest { [Fact] diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsManifestTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsManifestTest.cs index a278a732193a..6957302e4830 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsManifestTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsManifestTest.cs @@ -253,11 +253,11 @@ public void ExcludesEndpoints_BasedOnExclusionPatterns() // Assert new FileInfo(path).Should().Exist(); new FileInfo(exclusionCachePath).Should().Exist(); - + var manifest = File.ReadAllText(path); var json = JsonSerializer.Deserialize(manifest); json.Should().NotBeNull(); - + // Only styles.css endpoint should remain as others match _content/MyApp/** json.Endpoints.Should().HaveCount(1); json.Endpoints[0].Route.Should().Contain("styles.css"); @@ -406,7 +406,7 @@ public void RegeneratesManifest_WhenExclusionPatternsChange() // Assert - File should be regenerated var secondWriteTime = File.GetLastWriteTimeUtc(endpointsManifestPath); secondWriteTime.Should().BeAfter(firstWriteTime); - + // Verify cache file was updated var cacheContent = File.ReadAllText(exclusionCachePath); cacheContent.Should().Contain("different/**"); diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsPropsFileTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsPropsFileTest.cs index 90135f0c14ca..6563cebc6bde 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsPropsFileTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsPropsFileTest.cs @@ -157,7 +157,7 @@ public void Fails_WhenEndpointWithoutAssetExists() result.Should().BeFalse(); errorMessages.Should().ContainSingle(); - errorMessages[0].Should().Be($"""The asset file '{Path.GetFullPath(Path.Combine("wwwroot", "js", "sample.js"))}' specified in the endpoint '{Path.Combine("js","sample.js").Replace('\\', '/')}' does not exist."""); + errorMessages[0].Should().Be($"""The asset file '{Path.GetFullPath(Path.Combine("wwwroot", "js", "sample.js"))}' specified in the endpoint '{Path.Combine("js", "sample.js").Replace('\\', '/')}' does not exist."""); } private static ITaskItem CreateStaticWebAsset( diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs index fb2746fb1ce5..b5f256aadfa9 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs @@ -27,29 +27,29 @@ namespace Microsoft.AspNetCore.StaticWebAssets.Tasks.Test; // Recursive wildcard in the middle 'a/**/c' public partial class StaticWebAssetGlobMatcherTest { - [Theory] - [InlineData("**/*.razor.js", "Components/Pages/RegularComponent.razor.js", "Components/Pages/RegularComponent.razor.js")] - [InlineData("**/*.razor.js", "Components/User.Profile.Details.razor.js", "Components/User.Profile.Details.razor.js")] - [InlineData("**/*.razor.js", "Components/Area/Sub/Feature/User.Profile.Details.razor.js", "Components/Area/Sub/Feature/User.Profile.Details.razor.js")] - [InlineData("**/*.razor.js", "Components/Area/Sub/Feature/Deep.Component.Name.With.Many.Parts.razor.js", "Components/Area/Sub/Feature/Deep.Component.Name.With.Many.Parts.razor.js")] - [InlineData("**/*.cshtml.js", "Pages/Shared/_Host.cshtml.js", "Pages/Shared/_Host.cshtml.js")] - [InlineData("**/*.cshtml.js", "Areas/Admin/Pages/Dashboard.cshtml.js", "Areas/Admin/Pages/Dashboard.cshtml.js")] - [InlineData("*.lib.module.js", "Widget.lib.module.js", "Widget.lib.module.js")] - [InlineData("*.razor.css", "Component.razor.css", "Component.razor.css")] - [InlineData("*.cshtml.css", "View.cshtml.css", "View.cshtml.css")] - [InlineData("*.modules.json", "app.modules.json", "app.modules.json")] - [InlineData("*.lib.module.js", "Rcl.Client.Feature.lib.module.js", "Rcl.Client.Feature.lib.module.js")] - public void Can_Match_WellKnownExistingPatterns(string pattern, string path, string expectedStem) - { - var matcher = new StaticWebAssetGlobMatcherBuilder(); - matcher.AddIncludePatterns(pattern); - var globMatcher = matcher.Build(); + [Theory] + [InlineData("**/*.razor.js", "Components/Pages/RegularComponent.razor.js", "Components/Pages/RegularComponent.razor.js")] + [InlineData("**/*.razor.js", "Components/User.Profile.Details.razor.js", "Components/User.Profile.Details.razor.js")] + [InlineData("**/*.razor.js", "Components/Area/Sub/Feature/User.Profile.Details.razor.js", "Components/Area/Sub/Feature/User.Profile.Details.razor.js")] + [InlineData("**/*.razor.js", "Components/Area/Sub/Feature/Deep.Component.Name.With.Many.Parts.razor.js", "Components/Area/Sub/Feature/Deep.Component.Name.With.Many.Parts.razor.js")] + [InlineData("**/*.cshtml.js", "Pages/Shared/_Host.cshtml.js", "Pages/Shared/_Host.cshtml.js")] + [InlineData("**/*.cshtml.js", "Areas/Admin/Pages/Dashboard.cshtml.js", "Areas/Admin/Pages/Dashboard.cshtml.js")] + [InlineData("*.lib.module.js", "Widget.lib.module.js", "Widget.lib.module.js")] + [InlineData("*.razor.css", "Component.razor.css", "Component.razor.css")] + [InlineData("*.cshtml.css", "View.cshtml.css", "View.cshtml.css")] + [InlineData("*.modules.json", "app.modules.json", "app.modules.json")] + [InlineData("*.lib.module.js", "Rcl.Client.Feature.lib.module.js", "Rcl.Client.Feature.lib.module.js")] + public void Can_Match_WellKnownExistingPatterns(string pattern, string path, string expectedStem) + { + var matcher = new StaticWebAssetGlobMatcherBuilder(); + matcher.AddIncludePatterns(pattern); + var globMatcher = matcher.Build(); - var match = globMatcher.Match(path); - Assert.True(match.IsMatch); - Assert.Equal(pattern, match.Pattern); - Assert.Equal(expectedStem, match.Stem); - } + var match = globMatcher.Match(path); + Assert.True(match.IsMatch); + Assert.Equal(pattern, match.Pattern); + Assert.Equal(expectedStem, match.Stem); + } [Fact] public void CanMatchLiterals() { @@ -175,7 +175,7 @@ public void QuestionMarksMatchSingleCharacter(string pattern, string input, bool var globMatcher = matcher.Build(); var match = globMatcher.Match(input); Assert.Equal(expectedMatchResult, match.IsMatch); - if(expectedMatchResult) + if (expectedMatchResult) { Assert.Equal(pattern, match.Pattern); Assert.Equal(input, match.Stem); diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs index 31e709b85f12..28cc56b69570 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs @@ -3,8 +3,8 @@ #nullable disable -using Microsoft.AspNetCore.StaticWebAssets.Tasks; using System.Text.RegularExpressions; +using Microsoft.AspNetCore.StaticWebAssets.Tasks; namespace Microsoft.AspNetCore.Razor.Tasks; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveCompressedAssetsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveCompressedAssetsTest.cs index 5098c8ac0738..7d5eff350ce2 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveCompressedAssetsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveCompressedAssetsTest.cs @@ -96,7 +96,7 @@ public void InfersPreCompressedAssetsCorrectly() CopyToOutputDirectory = StaticWebAsset.AssetCopyOptions.Never, Fingerprint = "xtxxf3hu2r", RelatedAsset = string.Empty, - ContentRoot = Path.Combine(Environment.CurrentDirectory,"wwwroot"), + ContentRoot = Path.Combine(Environment.CurrentDirectory, "wwwroot"), SourceType = StaticWebAsset.SourceTypes.Discovered, Integrity = "hRQyftXiu1lLX2P9Ly9xa4gHJgLeR1uGN5qegUobtGo=", FileLength = 10, @@ -206,7 +206,7 @@ public void ResolvesAssets_WithFingerprint_MatchingIncludePattern() { Identity = ItemSpec, OriginalItemSpec = OriginalItemSpec, - RelativePath = Path.GetFileNameWithoutExtension(ItemSpec)+"#[.{fingerprint}]" + Path.GetExtension(ItemSpec), + RelativePath = Path.GetFileNameWithoutExtension(ItemSpec) + "#[.{fingerprint}]" + Path.GetExtension(ItemSpec), ContentRoot = Path.GetDirectoryName(ItemSpec), SourceType = StaticWebAsset.SourceTypes.Discovered, SourceId = "App", diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveFingerprintedStaticWebAssetEndpointsForAssetsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveFingerprintedStaticWebAssetEndpointsForAssetsTest.cs index a27049b45841..6e7f5875090f 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveFingerprintedStaticWebAssetEndpointsForAssetsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveFingerprintedStaticWebAssetEndpointsForAssetsTest.cs @@ -41,7 +41,7 @@ public void Standalone_Selects_EndpointMatching_FilePath(string pattern, string var resolvedEndpoints = new ResolveFingerprintedStaticWebAssetEndpointsForAssets { CandidateAssets = [.. candidateAssets], - CandidateEndpoints = [..endpoints.Select(e => e.ToTaskItem())], + CandidateEndpoints = [.. endpoints.Select(e => e.ToTaskItem())], IsStandalone = true, BuildEngine = buildEngine.Object }; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateExternallyDefinedStaticWebAssetsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateExternallyDefinedStaticWebAssetsTest.cs index 9c2e008f4610..a57f76a816de 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateExternallyDefinedStaticWebAssetsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateExternallyDefinedStaticWebAssetsTest.cs @@ -23,7 +23,7 @@ public void Execute_UpdatesAssetsWithoutFingerprint() Directory.CreateDirectory(Path.Combine(AppContext.BaseDirectory, "dist", "assets")); File.WriteAllText(Path.Combine(AppContext.BaseDirectory, "dist", "assets", "index-C5tBAdQX.css"), "body { color: red; }"); File.WriteAllText(Path.Combine(AppContext.BaseDirectory, "dist", "index.html"), ""); - var assets = new ITaskItem [] { + var assets = new ITaskItem[] { new TaskItem( Path.Combine(AppContext.BaseDirectory, @"dist\assets\index-C5tBAdQX.css"), new Dictionary diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateStaticWebAssetEndpointsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateStaticWebAssetEndpointsTest.cs index 43956cf963c3..8fbf56545498 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateStaticWebAssetEndpointsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateStaticWebAssetEndpointsTest.cs @@ -256,7 +256,7 @@ public void CanUpdateEndpoint_RetainsNonModifiedEndpointsWithSameRoute() EndpointProperties = [.. fingerprintedEndpoints[0].EndpointProperties] }; - endpoints = [..endpoints, unmodifiedEndpoint]; + endpoints = [.. endpoints, unmodifiedEndpoint]; foreach (var endpoint in fingerprintedEndpoints) { diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsBaselineFactory.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsBaselineFactory.cs index 8d0a563a0b83..d3e1a3f0b80b 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsBaselineFactory.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsBaselineFactory.cs @@ -10,6 +10,7 @@ using NuGet.ProjectModel; namespace Microsoft.NET.Sdk.StaticWebAssets.Tests; + public partial class StaticWebAssetsBaselineFactory { [GeneratedRegex("""(.*\.)([0123456789abcdefghijklmnopqrstuvwxyz]{10})(\.bundle\.scp\.css)((?:\.gz)|(?:\.br))?$""", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsCompressionIntegrationTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsCompressionIntegrationTest.cs index df2caf4709da..12b39c795d7c 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsCompressionIntegrationTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsCompressionIntegrationTest.cs @@ -74,7 +74,7 @@ public void Build_Detects_PrecompressedAssets() endpoint.ResponseHeaders.Where(e => e.Name == "Content-Encoding").Select(e => e.Value).Single().Should().Be("gzip"); var etags = endpoint.ResponseHeaders.Where(e => e.Name == "ETag").Select(e => EntityTagHeaderValue.Parse(e.Value)); - etags.Where(e=> !e.IsWeak).Select(e => e.Tag).Single().Should().BeEquivalentTo($"\"{gzipAsset.Integrity}\""); + etags.Where(e => !e.IsWeak).Select(e => e.Tag).Single().Should().BeEquivalentTo($"\"{gzipAsset.Integrity}\""); if (endpoint.Route.EndsWith(".gz")) { continue; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsFingerprintingTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsFingerprintingTest.cs index e9af1e2755ad..9385832c66d7 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsFingerprintingTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsFingerprintingTest.cs @@ -3,9 +3,9 @@ #nullable disable -using Microsoft.AspNetCore.StaticWebAssets.Tasks; -using System.Text.Json; using System.IO.Compression; +using System.Text.Json; +using Microsoft.AspNetCore.StaticWebAssets.Tasks; namespace Microsoft.NET.Sdk.StaticWebAssets.Tests; @@ -17,7 +17,8 @@ public void Build_FingerprintsContent_WhenEnabled() var expectedManifest = LoadBuildManifest(); var testAsset = "RazorComponentApp"; ProjectDirectory = CreateAspNetSdkTestAsset(testAsset) - .WithProjectChanges(p => { + .WithProjectChanges(p => + { var fingerprintContent = p.Descendants() .SingleOrDefault(e => e.Name.LocalName == "StaticWebAssetsFingerprintContent"); fingerprintContent.Value = "true"; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsIntegrationTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsIntegrationTest.cs index c13c8b2d5c6b..4e2fa680a127 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsIntegrationTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsIntegrationTest.cs @@ -751,7 +751,7 @@ public void BuildProjectWithReferences_NoDependencies_GeneratesJsonManifestAndCo // Second build var secondBuild = CreateBuildCommand(ProjectDirectory, "AppWithPackageAndP2PReference"); - ExecuteCommand(secondBuild,"/p:BuildProjectReferences=false").Should().Pass(); + ExecuteCommand(secondBuild, "/p:BuildProjectReferences=false").Should().Pass(); // GenerateStaticWebAssetsManifest should generate the manifest file. new FileInfo(path).Should().Exist(); From f44a784cce295cfccadd1ac0f02b78c37a46f4e0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Sep 2025 12:41:15 +0000 Subject: [PATCH 3/6] Address feedback: revert unnecessary changes and fix test to use proper project structure Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com> --- ....NET.Sdk.StaticWebAssets.ScopedCss.targets | 8 +-- .../ComputeStaticWebAssetsTargetPathsTest.cs | 1 - .../JsModulesIntegrationTest.cs | 14 +++--- .../LegacyStaticWebAssetsV1IntegrationTest.cs | 2 +- .../ScopedCssIntegrationTests.cs | 50 ++++++++++++++----- .../StaticWebAssetEndpointsIntegrationTest.cs | 48 +++++++++--------- .../DefineStaticWebAssetEndpointsTest.cs | 6 +-- .../DiscoverPrecompressedAssetsTest.cs | 2 +- .../DiscoverStaticWebAssetsTest.cs | 8 +-- .../FilterStaticWebAssetEndpointsTest.cs | 1 - ...rateStaticWebAssetEndpointsManifestTest.cs | 6 +-- ...ateStaticWebAssetEndpointsPropsFileTest.cs | 2 +- .../Globbing/StaticWebAssetGlobMatcherTest.cs | 46 ++++++++--------- .../OverrideHtmlAssetPlaceholdersTest.cs | 2 +- .../ResolveCompressedAssetsTest.cs | 4 +- ...tedStaticWebAssetEndpointsForAssetsTest.cs | 2 +- ...ateExternallyDefinedStaticWebAssetsTest.cs | 2 +- .../UpdateStaticWebAssetEndpointsTest.cs | 2 +- .../StaticWebAssetsBaselineFactory.cs | 1 - ...aticWebAssetsCompressionIntegrationTest.cs | 2 +- .../StaticWebAssetsFingerprintingTest.cs | 7 ++- .../StaticWebAssetsIntegrationTest.cs | 2 +- 22 files changed, 119 insertions(+), 99 deletions(-) diff --git a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets index 6a415883af2f..60fa20ec13ce 100644 --- a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets +++ b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets @@ -233,12 +233,12 @@ Integration with static web assets: <_ScopedCssAppBundleStaticWebAssetCandidate Include="$(_ScopedCssOutputPath)"> - $([System.Uri]::EscapeDataString($(PackageId)))#[.{fingerprint}]?.styles.css - $([System.Uri]::EscapeDataString($(PackageId))).styles.css + $(PackageId)#[.{fingerprint}]?.styles.css + $(PackageId).styles.css <_ScopedCssProjectBundleStaticWebAssetCandidate Include="$(_ScopedCssProjectOutputPath)"> - $([System.Uri]::EscapeDataString($(PackageId)))#[.{fingerprint}]!.bundle.scp.css - $([System.Uri]::EscapeDataString($(PackageId))).bundle.scp.css + $(PackageId)#[.{fingerprint}]!.bundle.scp.css + $(PackageId).bundle.scp.css <_AddLinkHeaderToAppBundle Include="Append" Condition="'@(_ResolvedScopedCssBundleEndpoints)' != ''"> diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ComputeStaticWebAssetsTargetPathsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ComputeStaticWebAssetsTargetPathsTest.cs index 114a097d7c63..8bf1bafb89dd 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ComputeStaticWebAssetsTargetPathsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ComputeStaticWebAssetsTargetPathsTest.cs @@ -13,7 +13,6 @@ using Moq; namespace Microsoft.NET.Sdk.StaticWebAssets.Tests; - public class ComputeStaticWebAssetsTargetPathsTest { [Fact] diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/JsModulesIntegrationTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/JsModulesIntegrationTest.cs index 56ad90dfc8ed..d96dab0d59e5 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/JsModulesIntegrationTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/JsModulesIntegrationTest.cs @@ -31,12 +31,11 @@ public void Build_GeneratesManifestWhenItFindsALibrary() { var testAsset = "RazorComponentApp"; var projectDirectory = CreateAspNetSdkTestAsset(testAsset) - .WithProjectChanges(p => - { - var fingerprintContent = p.Descendants() - .SingleOrDefault(e => e.Name.LocalName == "StaticWebAssetsFingerprintContent"); - fingerprintContent.Value = "true"; - }); + .WithProjectChanges(p => { + var fingerprintContent = p.Descendants() + .SingleOrDefault(e => e.Name.LocalName == "StaticWebAssetsFingerprintContent"); + fingerprintContent.Value = "true"; + }); Directory.CreateDirectory(Path.Combine(projectDirectory.TestRoot, "wwwroot")); File.WriteAllText(Path.Combine(projectDirectory.TestRoot, "wwwroot", "ComponentApp.lib.module.js"), "console.log('Hello world!');"); @@ -90,8 +89,7 @@ public void Publish_PublishesJsModuleBundleBundleToTheRightLocation() { var testAsset = "RazorComponentApp"; ProjectDirectory = CreateAspNetSdkTestAsset(testAsset) - .WithProjectChanges(p => - { + .WithProjectChanges(p => { var fingerprintContent = p.Descendants() .SingleOrDefault(e => e.Name.LocalName == "StaticWebAssetsFingerprintContent"); fingerprintContent.Value = "true"; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/LegacyStaticWebAssetsV1IntegrationTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/LegacyStaticWebAssetsV1IntegrationTest.cs index 248b025ac4a5..9089ed148d04 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/LegacyStaticWebAssetsV1IntegrationTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/LegacyStaticWebAssetsV1IntegrationTest.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.StaticWebAssets.Tasks; -[assembly: CollectionBehavior(DisableTestParallelization = true)] +[assembly:CollectionBehavior(DisableTestParallelization = true)] namespace Microsoft.NET.Sdk.StaticWebAssets.Tests { diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs index 245976d4cabe..dbbfbb61d717 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs @@ -621,17 +621,46 @@ public void RegeneratingScopedCss_ForProjectWithReferences() [Fact] public void Build_GeneratesUrlEncodedLinkHeaderForNonAsciiProjectName() { - var testAsset = "RazorComponentApp"; - var projectDirectory = CreateAspNetSdkTestAsset(testAsset); + var testAsset = "RazorAppWithPackageAndP2PReference"; + ProjectDirectory = CreateAspNetSdkTestAsset(testAsset); - // Create a CSS file to trigger scoped CSS processing - var cssFile = Path.Combine(projectDirectory.Path, "Components", "Pages", "Index.razor.css"); - Directory.CreateDirectory(Path.GetDirectoryName(cssFile)); - File.WriteAllText(cssFile, ".test { color: red; }"); + // Rename the ClassLibrary project to have non-ASCII characters + var originalLibPath = Path.Combine(ProjectDirectory.Path, "ClassLibrary"); + var newLibPath = Path.Combine(ProjectDirectory.Path, "项目"); + Directory.Move(originalLibPath, newLibPath); + + // Update the project file to set the assembly name and package ID + var libProjectFile = Path.Combine(newLibPath, "ClassLibrary.csproj"); + var newLibProjectFile = Path.Combine(newLibPath, "项目.csproj"); + File.Move(libProjectFile, newLibProjectFile); + + // Add assembly name property to ensure consistent naming + var libProjectContent = File.ReadAllText(newLibProjectFile); + libProjectContent = libProjectContent.Replace("", + " 项目\n 项目\n ", 1); + File.WriteAllText(newLibProjectFile, libProjectContent); + + // Update the main project to reference the renamed library + var mainProjectFile = Path.Combine(ProjectDirectory.Path, "AppWithPackageAndP2PReference", "AppWithPackageAndP2PReference.csproj"); + var mainProjectContent = File.ReadAllText(mainProjectFile); + mainProjectContent = mainProjectContent.Replace(@"..\ClassLibrary\ClassLibrary.csproj", @"..\项目\项目.csproj"); + File.WriteAllText(mainProjectFile, mainProjectContent); + + // Ensure library has scoped CSS + var libCssFile = Path.Combine(newLibPath, "Components", "Component1.razor.css"); + if (!File.Exists(libCssFile)) + { + Directory.CreateDirectory(Path.GetDirectoryName(libCssFile)); + File.WriteAllText(libCssFile, ".test { color: red; }"); + } - var build = CreateBuildCommand(projectDirectory); - // Set PackageId to contain non-ASCII characters (Chinese characters meaning "project") - ExecuteCommand(build, "/p:PackageId=项目").Should().Pass(); + EnsureLocalPackagesExists(); + + var restore = CreateRestoreCommand(ProjectDirectory, "AppWithPackageAndP2PReference"); + ExecuteCommand(restore).Should().Pass(); + + var build = CreateBuildCommand(ProjectDirectory, "AppWithPackageAndP2PReference"); + ExecuteCommand(build).Should().Pass(); var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString(); @@ -643,9 +672,6 @@ public void Build_GeneratesUrlEncodedLinkHeaderForNonAsciiProjectName() // Verify that the Link header contains URL-encoded characters (%E9%A1%B9%E7%9B%AE is "项目" encoded) endpointsContent.Should().Contain("%E9%A1%B9%E7%9B%AE"); - - // Verify it doesn't contain the unencoded characters - endpointsContent.Should().NotContain("项目"); } } } diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetEndpointsIntegrationTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetEndpointsIntegrationTest.cs index ade219c5f8f4..79442747cab9 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetEndpointsIntegrationTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetEndpointsIntegrationTest.cs @@ -85,9 +85,9 @@ private bool MatchUncompresedProjectBundlesNoFingerprint(StaticWebAssetEndpoint Success: true, Groups: [ var _, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: "", Success: false }, - { Name: "compress", Value: "", Success: false } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: "", Success: false }, + { Name: "compress", Value: "", Success: false } ] }; @@ -96,9 +96,9 @@ private bool MatchCompressedProjectBundlesNoFingerprint(StaticWebAssetEndpoint e Success: true, Groups: [ var _, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: "", Success: false }, - { Name: "compress", Value: var compress, Success: true } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: "", Success: false }, + { Name: "compress", Value: var compress, Success: true } ] } && (compress == ".gz" || compress == ".br"); @@ -107,9 +107,9 @@ private bool MatchUncompressedProjectBundlesWithFingerprint(StaticWebAssetEndpoi Success: true, Groups: [ var m, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: var fingerprint, Success: true }, - { Name: "compress", Value: "", Success: false } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: var fingerprint, Success: true }, + { Name: "compress", Value: "", Success: false } ] } && fingerprint == ep.EndpointProperties.Single(p => p.Name == "fingerprint").Value; @@ -118,9 +118,9 @@ private bool MatchCompressedProjectBundlesWithFingerprint(StaticWebAssetEndpoint Success: true, Groups: [ var m, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: var fingerprint, Success: true }, - { Name: "compress", Value: var compress, Success: true } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: var fingerprint, Success: true }, + { Name: "compress", Value: var compress, Success: true } ] } && !string.IsNullOrWhiteSpace(fingerprint) && (compress == ".gz" || compress == ".br"); @@ -130,9 +130,9 @@ private bool MatchUncompressedAppBundleNoFingerprint(StaticWebAssetEndpoint ep) Success: true, Groups: [ var _, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: "", Success: false }, - { Name: "compress", Value: "", Success: false } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: "", Success: false }, + { Name: "compress", Value: "", Success: false } ] }; @@ -141,9 +141,9 @@ private bool MatchCompressedAppBundleNoFingerprint(StaticWebAssetEndpoint ep) => Success: true, Groups: [ var _, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: "", Success: false }, - { Name: "compress", Value: var compress, Success: true } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: "", Success: false }, + { Name: "compress", Value: var compress, Success: true } ] } && (compress == ".gz" || compress == ".br"); @@ -152,9 +152,9 @@ private bool MatchUncompressedAppBundleWithFingerprint(StaticWebAssetEndpoint ep Success: true, Groups: [ var m, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: var fingerprint, Success: true }, - { Name: "compress", Value: "", Success: false } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: var fingerprint, Success: true }, + { Name: "compress", Value: "", Success: false } ] } && fingerprint == ep.EndpointProperties.Single(p => p.Name == "fingerprint").Value; @@ -163,9 +163,9 @@ private bool MatchCompressedAppBundleWithFingerprint(StaticWebAssetEndpoint ep) Success: true, Groups: [ var m, - { Name: "project", Value: "ComponentApp", Success: true, }, - { Name: "fingerprint", Value: var fingerprint, Success: true }, - { Name: "compress", Value: var compress, Success: true } + { Name: "project", Value: "ComponentApp", Success: true, }, + { Name: "fingerprint", Value: var fingerprint, Success: true }, + { Name: "compress", Value: var compress, Success: true } ] } && !string.IsNullOrWhiteSpace(fingerprint) && (compress == ".gz" || compress == ".br"); diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DefineStaticWebAssetEndpointsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DefineStaticWebAssetEndpointsTest.cs index ccab6a4fe566..dfb709b5865d 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DefineStaticWebAssetEndpointsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DefineStaticWebAssetEndpointsTest.cs @@ -3,15 +3,15 @@ #nullable disable -using System.Diagnostics; using System.Diagnostics.Metrics; -using System.Globalization; -using System.Net; +using System.Diagnostics; using Microsoft.AspNetCore.StaticWebAssets.Tasks; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Moq; using NuGet.Packaging.Core; +using System.Net; +using System.Globalization; namespace Microsoft.NET.Sdk.StaticWebAssets.Tests; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverPrecompressedAssetsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverPrecompressedAssetsTest.cs index 5eccf523e3e7..27d7baae60f0 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverPrecompressedAssetsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverPrecompressedAssetsTest.cs @@ -44,7 +44,7 @@ public void DiscoversPrecompressedAssetsCorrectly() CopyToOutputDirectory = StaticWebAsset.AssetCopyOptions.Never, Fingerprint = "uncompressed", RelatedAsset = string.Empty, - ContentRoot = Path.Combine(Environment.CurrentDirectory, "wwwroot"), + ContentRoot = Path.Combine(Environment.CurrentDirectory,"wwwroot"), SourceType = StaticWebAsset.SourceTypes.Discovered, Integrity = "uncompressed-integrity", AssetRole = StaticWebAsset.AssetRoles.Primary, diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs index e56d53b8b7d2..f252022d1338 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs @@ -217,9 +217,9 @@ public void FingerprintsContentUsingPatternsWhenMoreThanOneExtension(string file asset.GetMetadata(nameof(StaticWebAsset.OriginalItemSpec)).Should().Be(Path.Combine("wwwroot", fileName)); } - [Fact] - [Trait("Category", "FingerprintIdentity")] - public void ComputesIdentity_UsingFingerprintPattern_ForComputedAssets_WhenIdentityNeedsComputation() + [Fact] + [Trait("Category", "FingerprintIdentity")] + public void ComputesIdentity_UsingFingerprintPattern_ForComputedAssets_WhenIdentityNeedsComputation() { // Arrange: simulate a packaged asset (outside content root) with a RelativePath inside the app var errorMessages = new List(); @@ -251,7 +251,7 @@ public void ComputesIdentity_UsingFingerprintPattern_ForComputedAssets_WhenIdent }) ], // No RelativePathPattern, we trigger the branch that synthesizes identity under content root. - FingerprintPatterns = [new TaskItem("Js", new Dictionary { { "Pattern", "*.js" }, { "Expression", "#[.{fingerprint}]!" } })], + FingerprintPatterns = [ new TaskItem("Js", new Dictionary{{"Pattern","*.js"},{"Expression","#[.{fingerprint}]!"}})], FingerprintCandidates = true, SourceType = "Computed", SourceId = "Client", diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/FilterStaticWebAssetEndpointsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/FilterStaticWebAssetEndpointsTest.cs index 7b8a19394706..2cf9ee746d4e 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/FilterStaticWebAssetEndpointsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/FilterStaticWebAssetEndpointsTest.cs @@ -9,7 +9,6 @@ using Moq; namespace Microsoft.NET.Sdk.StaticWebAssets.Tests.StaticWebAssets; - public class FilterStaticWebAssetEndpointsTest { [Fact] diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsManifestTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsManifestTest.cs index 6957302e4830..a278a732193a 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsManifestTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsManifestTest.cs @@ -253,11 +253,11 @@ public void ExcludesEndpoints_BasedOnExclusionPatterns() // Assert new FileInfo(path).Should().Exist(); new FileInfo(exclusionCachePath).Should().Exist(); - + var manifest = File.ReadAllText(path); var json = JsonSerializer.Deserialize(manifest); json.Should().NotBeNull(); - + // Only styles.css endpoint should remain as others match _content/MyApp/** json.Endpoints.Should().HaveCount(1); json.Endpoints[0].Route.Should().Contain("styles.css"); @@ -406,7 +406,7 @@ public void RegeneratesManifest_WhenExclusionPatternsChange() // Assert - File should be regenerated var secondWriteTime = File.GetLastWriteTimeUtc(endpointsManifestPath); secondWriteTime.Should().BeAfter(firstWriteTime); - + // Verify cache file was updated var cacheContent = File.ReadAllText(exclusionCachePath); cacheContent.Should().Contain("different/**"); diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsPropsFileTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsPropsFileTest.cs index 6563cebc6bde..90135f0c14ca 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsPropsFileTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/GenerateStaticWebAssetEndpointsPropsFileTest.cs @@ -157,7 +157,7 @@ public void Fails_WhenEndpointWithoutAssetExists() result.Should().BeFalse(); errorMessages.Should().ContainSingle(); - errorMessages[0].Should().Be($"""The asset file '{Path.GetFullPath(Path.Combine("wwwroot", "js", "sample.js"))}' specified in the endpoint '{Path.Combine("js", "sample.js").Replace('\\', '/')}' does not exist."""); + errorMessages[0].Should().Be($"""The asset file '{Path.GetFullPath(Path.Combine("wwwroot", "js", "sample.js"))}' specified in the endpoint '{Path.Combine("js","sample.js").Replace('\\', '/')}' does not exist."""); } private static ITaskItem CreateStaticWebAsset( diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs index b5f256aadfa9..fb2746fb1ce5 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs @@ -27,29 +27,29 @@ namespace Microsoft.AspNetCore.StaticWebAssets.Tasks.Test; // Recursive wildcard in the middle 'a/**/c' public partial class StaticWebAssetGlobMatcherTest { - [Theory] - [InlineData("**/*.razor.js", "Components/Pages/RegularComponent.razor.js", "Components/Pages/RegularComponent.razor.js")] - [InlineData("**/*.razor.js", "Components/User.Profile.Details.razor.js", "Components/User.Profile.Details.razor.js")] - [InlineData("**/*.razor.js", "Components/Area/Sub/Feature/User.Profile.Details.razor.js", "Components/Area/Sub/Feature/User.Profile.Details.razor.js")] - [InlineData("**/*.razor.js", "Components/Area/Sub/Feature/Deep.Component.Name.With.Many.Parts.razor.js", "Components/Area/Sub/Feature/Deep.Component.Name.With.Many.Parts.razor.js")] - [InlineData("**/*.cshtml.js", "Pages/Shared/_Host.cshtml.js", "Pages/Shared/_Host.cshtml.js")] - [InlineData("**/*.cshtml.js", "Areas/Admin/Pages/Dashboard.cshtml.js", "Areas/Admin/Pages/Dashboard.cshtml.js")] - [InlineData("*.lib.module.js", "Widget.lib.module.js", "Widget.lib.module.js")] - [InlineData("*.razor.css", "Component.razor.css", "Component.razor.css")] - [InlineData("*.cshtml.css", "View.cshtml.css", "View.cshtml.css")] - [InlineData("*.modules.json", "app.modules.json", "app.modules.json")] - [InlineData("*.lib.module.js", "Rcl.Client.Feature.lib.module.js", "Rcl.Client.Feature.lib.module.js")] - public void Can_Match_WellKnownExistingPatterns(string pattern, string path, string expectedStem) - { - var matcher = new StaticWebAssetGlobMatcherBuilder(); - matcher.AddIncludePatterns(pattern); - var globMatcher = matcher.Build(); + [Theory] + [InlineData("**/*.razor.js", "Components/Pages/RegularComponent.razor.js", "Components/Pages/RegularComponent.razor.js")] + [InlineData("**/*.razor.js", "Components/User.Profile.Details.razor.js", "Components/User.Profile.Details.razor.js")] + [InlineData("**/*.razor.js", "Components/Area/Sub/Feature/User.Profile.Details.razor.js", "Components/Area/Sub/Feature/User.Profile.Details.razor.js")] + [InlineData("**/*.razor.js", "Components/Area/Sub/Feature/Deep.Component.Name.With.Many.Parts.razor.js", "Components/Area/Sub/Feature/Deep.Component.Name.With.Many.Parts.razor.js")] + [InlineData("**/*.cshtml.js", "Pages/Shared/_Host.cshtml.js", "Pages/Shared/_Host.cshtml.js")] + [InlineData("**/*.cshtml.js", "Areas/Admin/Pages/Dashboard.cshtml.js", "Areas/Admin/Pages/Dashboard.cshtml.js")] + [InlineData("*.lib.module.js", "Widget.lib.module.js", "Widget.lib.module.js")] + [InlineData("*.razor.css", "Component.razor.css", "Component.razor.css")] + [InlineData("*.cshtml.css", "View.cshtml.css", "View.cshtml.css")] + [InlineData("*.modules.json", "app.modules.json", "app.modules.json")] + [InlineData("*.lib.module.js", "Rcl.Client.Feature.lib.module.js", "Rcl.Client.Feature.lib.module.js")] + public void Can_Match_WellKnownExistingPatterns(string pattern, string path, string expectedStem) + { + var matcher = new StaticWebAssetGlobMatcherBuilder(); + matcher.AddIncludePatterns(pattern); + var globMatcher = matcher.Build(); - var match = globMatcher.Match(path); - Assert.True(match.IsMatch); - Assert.Equal(pattern, match.Pattern); - Assert.Equal(expectedStem, match.Stem); - } + var match = globMatcher.Match(path); + Assert.True(match.IsMatch); + Assert.Equal(pattern, match.Pattern); + Assert.Equal(expectedStem, match.Stem); + } [Fact] public void CanMatchLiterals() { @@ -175,7 +175,7 @@ public void QuestionMarksMatchSingleCharacter(string pattern, string input, bool var globMatcher = matcher.Build(); var match = globMatcher.Match(input); Assert.Equal(expectedMatchResult, match.IsMatch); - if (expectedMatchResult) + if(expectedMatchResult) { Assert.Equal(pattern, match.Pattern); Assert.Equal(input, match.Stem); diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs index 28cc56b69570..31e709b85f12 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs @@ -3,8 +3,8 @@ #nullable disable -using System.Text.RegularExpressions; using Microsoft.AspNetCore.StaticWebAssets.Tasks; +using System.Text.RegularExpressions; namespace Microsoft.AspNetCore.Razor.Tasks; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveCompressedAssetsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveCompressedAssetsTest.cs index 7d5eff350ce2..5098c8ac0738 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveCompressedAssetsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveCompressedAssetsTest.cs @@ -96,7 +96,7 @@ public void InfersPreCompressedAssetsCorrectly() CopyToOutputDirectory = StaticWebAsset.AssetCopyOptions.Never, Fingerprint = "xtxxf3hu2r", RelatedAsset = string.Empty, - ContentRoot = Path.Combine(Environment.CurrentDirectory, "wwwroot"), + ContentRoot = Path.Combine(Environment.CurrentDirectory,"wwwroot"), SourceType = StaticWebAsset.SourceTypes.Discovered, Integrity = "hRQyftXiu1lLX2P9Ly9xa4gHJgLeR1uGN5qegUobtGo=", FileLength = 10, @@ -206,7 +206,7 @@ public void ResolvesAssets_WithFingerprint_MatchingIncludePattern() { Identity = ItemSpec, OriginalItemSpec = OriginalItemSpec, - RelativePath = Path.GetFileNameWithoutExtension(ItemSpec) + "#[.{fingerprint}]" + Path.GetExtension(ItemSpec), + RelativePath = Path.GetFileNameWithoutExtension(ItemSpec)+"#[.{fingerprint}]" + Path.GetExtension(ItemSpec), ContentRoot = Path.GetDirectoryName(ItemSpec), SourceType = StaticWebAsset.SourceTypes.Discovered, SourceId = "App", diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveFingerprintedStaticWebAssetEndpointsForAssetsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveFingerprintedStaticWebAssetEndpointsForAssetsTest.cs index 6e7f5875090f..a27049b45841 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveFingerprintedStaticWebAssetEndpointsForAssetsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/ResolveFingerprintedStaticWebAssetEndpointsForAssetsTest.cs @@ -41,7 +41,7 @@ public void Standalone_Selects_EndpointMatching_FilePath(string pattern, string var resolvedEndpoints = new ResolveFingerprintedStaticWebAssetEndpointsForAssets { CandidateAssets = [.. candidateAssets], - CandidateEndpoints = [.. endpoints.Select(e => e.ToTaskItem())], + CandidateEndpoints = [..endpoints.Select(e => e.ToTaskItem())], IsStandalone = true, BuildEngine = buildEngine.Object }; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateExternallyDefinedStaticWebAssetsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateExternallyDefinedStaticWebAssetsTest.cs index a57f76a816de..9c2e008f4610 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateExternallyDefinedStaticWebAssetsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateExternallyDefinedStaticWebAssetsTest.cs @@ -23,7 +23,7 @@ public void Execute_UpdatesAssetsWithoutFingerprint() Directory.CreateDirectory(Path.Combine(AppContext.BaseDirectory, "dist", "assets")); File.WriteAllText(Path.Combine(AppContext.BaseDirectory, "dist", "assets", "index-C5tBAdQX.css"), "body { color: red; }"); File.WriteAllText(Path.Combine(AppContext.BaseDirectory, "dist", "index.html"), ""); - var assets = new ITaskItem[] { + var assets = new ITaskItem [] { new TaskItem( Path.Combine(AppContext.BaseDirectory, @"dist\assets\index-C5tBAdQX.css"), new Dictionary diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateStaticWebAssetEndpointsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateStaticWebAssetEndpointsTest.cs index 8fbf56545498..43956cf963c3 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateStaticWebAssetEndpointsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/UpdateStaticWebAssetEndpointsTest.cs @@ -256,7 +256,7 @@ public void CanUpdateEndpoint_RetainsNonModifiedEndpointsWithSameRoute() EndpointProperties = [.. fingerprintedEndpoints[0].EndpointProperties] }; - endpoints = [.. endpoints, unmodifiedEndpoint]; + endpoints = [..endpoints, unmodifiedEndpoint]; foreach (var endpoint in fingerprintedEndpoints) { diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsBaselineFactory.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsBaselineFactory.cs index d3e1a3f0b80b..8d0a563a0b83 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsBaselineFactory.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsBaselineFactory.cs @@ -10,7 +10,6 @@ using NuGet.ProjectModel; namespace Microsoft.NET.Sdk.StaticWebAssets.Tests; - public partial class StaticWebAssetsBaselineFactory { [GeneratedRegex("""(.*\.)([0123456789abcdefghijklmnopqrstuvwxyz]{10})(\.bundle\.scp\.css)((?:\.gz)|(?:\.br))?$""", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsCompressionIntegrationTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsCompressionIntegrationTest.cs index 12b39c795d7c..df2caf4709da 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsCompressionIntegrationTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsCompressionIntegrationTest.cs @@ -74,7 +74,7 @@ public void Build_Detects_PrecompressedAssets() endpoint.ResponseHeaders.Where(e => e.Name == "Content-Encoding").Select(e => e.Value).Single().Should().Be("gzip"); var etags = endpoint.ResponseHeaders.Where(e => e.Name == "ETag").Select(e => EntityTagHeaderValue.Parse(e.Value)); - etags.Where(e => !e.IsWeak).Select(e => e.Tag).Single().Should().BeEquivalentTo($"\"{gzipAsset.Integrity}\""); + etags.Where(e=> !e.IsWeak).Select(e => e.Tag).Single().Should().BeEquivalentTo($"\"{gzipAsset.Integrity}\""); if (endpoint.Route.EndsWith(".gz")) { continue; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsFingerprintingTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsFingerprintingTest.cs index 9385832c66d7..e9af1e2755ad 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsFingerprintingTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsFingerprintingTest.cs @@ -3,9 +3,9 @@ #nullable disable -using System.IO.Compression; -using System.Text.Json; using Microsoft.AspNetCore.StaticWebAssets.Tasks; +using System.Text.Json; +using System.IO.Compression; namespace Microsoft.NET.Sdk.StaticWebAssets.Tests; @@ -17,8 +17,7 @@ public void Build_FingerprintsContent_WhenEnabled() var expectedManifest = LoadBuildManifest(); var testAsset = "RazorComponentApp"; ProjectDirectory = CreateAspNetSdkTestAsset(testAsset) - .WithProjectChanges(p => - { + .WithProjectChanges(p => { var fingerprintContent = p.Descendants() .SingleOrDefault(e => e.Name.LocalName == "StaticWebAssetsFingerprintContent"); fingerprintContent.Value = "true"; diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsIntegrationTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsIntegrationTest.cs index 4e2fa680a127..c13c8b2d5c6b 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsIntegrationTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsIntegrationTest.cs @@ -751,7 +751,7 @@ public void BuildProjectWithReferences_NoDependencies_GeneratesJsonManifestAndCo // Second build var secondBuild = CreateBuildCommand(ProjectDirectory, "AppWithPackageAndP2PReference"); - ExecuteCommand(secondBuild, "/p:BuildProjectReferences=false").Should().Pass(); + ExecuteCommand(secondBuild,"/p:BuildProjectReferences=false").Should().Pass(); // GenerateStaticWebAssetsManifest should generate the manifest file. new FileInfo(path).Should().Exist(); From 21a64d78fc9ab92d9e7ab9a3ce3e64035eb756fa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Sep 2025 12:44:56 +0000 Subject: [PATCH 4/6] Fix syntax error in test method Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com> --- .../ScopedCssIntegrationTests.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs index dbbfbb61d717..1c05d38cf0dc 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs @@ -636,8 +636,14 @@ public void Build_GeneratesUrlEncodedLinkHeaderForNonAsciiProjectName() // Add assembly name property to ensure consistent naming var libProjectContent = File.ReadAllText(newLibProjectFile); - libProjectContent = libProjectContent.Replace("", - " 项目\n 项目\n ", 1); + // Find the first PropertyGroup closing tag and replace it + var targetPattern = ""; + var replacement = " 项目\n 项目\n "; + var index = libProjectContent.IndexOf(targetPattern); + if (index >= 0) + { + libProjectContent = libProjectContent.Substring(0, index) + replacement + libProjectContent.Substring(index + targetPattern.Length); + } File.WriteAllText(newLibProjectFile, libProjectContent); // Update the main project to reference the renamed library From 85554444a81e9a0f81c7327db49cea7dfe6bc177 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Sat, 27 Sep 2025 19:46:17 +0200 Subject: [PATCH 5/6] fix test --- ...t.NET.Sdk.StaticWebAssets.ScopedCss.targets | 2 +- .../ScopedCssIntegrationTests.cs | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets index 60fa20ec13ce..e74f0589b497 100644 --- a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets +++ b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.ScopedCss.targets @@ -244,7 +244,7 @@ Integration with static web assets: <_AddLinkHeaderToAppBundle Include="Append" Condition="'@(_ResolvedScopedCssBundleEndpoints)' != ''"> Header Link - @(_ResolvedScopedCssBundleEndpoints->'<$([System.Uri]::EscapeDataString(%(Identity)))>; rel="preload"; as="style"', ', ') + @(_ResolvedScopedCssBundleEndpoints->'<%(Identity)>; rel="preload"; as="style"', ', ') diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs index 1c05d38cf0dc..6155a3498987 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs @@ -3,6 +3,7 @@ #nullable disable +using System.Text.Json; using System.Text.RegularExpressions; using Microsoft.AspNetCore.StaticWebAssets.Tasks; @@ -625,12 +626,12 @@ public void Build_GeneratesUrlEncodedLinkHeaderForNonAsciiProjectName() ProjectDirectory = CreateAspNetSdkTestAsset(testAsset); // Rename the ClassLibrary project to have non-ASCII characters - var originalLibPath = Path.Combine(ProjectDirectory.Path, "ClassLibrary"); + var originalLibPath = Path.Combine(ProjectDirectory.Path, "AnotherClassLib"); var newLibPath = Path.Combine(ProjectDirectory.Path, "项目"); Directory.Move(originalLibPath, newLibPath); // Update the project file to set the assembly name and package ID - var libProjectFile = Path.Combine(newLibPath, "ClassLibrary.csproj"); + var libProjectFile = Path.Combine(newLibPath, "AnotherClassLib.csproj"); var newLibProjectFile = Path.Combine(newLibPath, "项目.csproj"); File.Move(libProjectFile, newLibProjectFile); @@ -649,11 +650,11 @@ public void Build_GeneratesUrlEncodedLinkHeaderForNonAsciiProjectName() // Update the main project to reference the renamed library var mainProjectFile = Path.Combine(ProjectDirectory.Path, "AppWithPackageAndP2PReference", "AppWithPackageAndP2PReference.csproj"); var mainProjectContent = File.ReadAllText(mainProjectFile); - mainProjectContent = mainProjectContent.Replace(@"..\ClassLibrary\ClassLibrary.csproj", @"..\项目\项目.csproj"); + mainProjectContent = mainProjectContent.Replace(@"..\AnotherClassLib\AnotherClassLib.csproj", @"..\项目\项目.csproj"); File.WriteAllText(mainProjectFile, mainProjectContent); // Ensure library has scoped CSS - var libCssFile = Path.Combine(newLibPath, "Components", "Component1.razor.css"); + var libCssFile = Path.Combine(newLibPath, "Views", "Shared", "Index.cshtml.css"); if (!File.Exists(libCssFile)) { Directory.CreateDirectory(Path.GetDirectoryName(libCssFile)); @@ -675,9 +676,14 @@ public void Build_GeneratesUrlEncodedLinkHeaderForNonAsciiProjectName() new FileInfo(endpointsFile).Should().Exist(); var endpointsContent = File.ReadAllText(endpointsFile); + var json = JsonSerializer.Deserialize(endpointsContent, new JsonSerializerOptions(JsonSerializerDefaults.Web)); - // Verify that the Link header contains URL-encoded characters (%E9%A1%B9%E7%9B%AE is "项目" encoded) - endpointsContent.Should().Contain("%E9%A1%B9%E7%9B%AE"); + var styles = json.Endpoints.Where(e => e.Route.EndsWith("styles.css")); + + foreach (var styleEndpoint in styles) + { + styleEndpoint.ResponseHeaders.Should().Contain(h => h.Name.Equals("Link", StringComparison.OrdinalIgnoreCase) && h.Value.Contains("%E9%A1%B9%E7%9B%AE")); + } } } } From 95dcef5b9d439e8c42e5fbe2c8e8db303caa311a Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Sat, 27 Sep 2025 20:24:53 +0200 Subject: [PATCH 6/6] Fix encoding when generating the manifest instead --- ...GenerateStaticWebAssetEndpointsManifest.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs b/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs index 0387bb131057..f996a75b7168 100644 --- a/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs +++ b/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs @@ -116,6 +116,8 @@ public override bool Execute() endpoint.AssetFile = asset.ResolvedAsset.ComputeTargetPath("", '/', StaticWebAssetTokenResolver.Instance); endpoint.Route = route; + EncodeLinkHeadersIfNeeded(endpoint); + Log.LogMessage(MessageImportance.Low, "Including endpoint '{0}' for asset '{1}' with final location '{2}'", endpoint.Route, endpoint.AssetFile, asset.TargetPath); } @@ -137,6 +139,48 @@ public override bool Execute() return !Log.HasLoggedErrors; } + private static void EncodeLinkHeadersIfNeeded(StaticWebAssetEndpoint endpoint) + { + for (var i = 0; i < endpoint.ResponseHeaders.Length; i++) + { + ref var header = ref endpoint.ResponseHeaders[i]; + if (!string.Equals(header.Name, "Link", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + var headerValues = header.Value.Split([','], StringSplitOptions.RemoveEmptyEntries); + for (var j = 0; j < headerValues.Length; j++) + { + ref var value = ref headerValues[j]; + value = EncodeHeaderValue(value); + } + header.Value = string.Join(",", headerValues); + } + } + + private static string EncodeHeaderValue(string header) + { + var index = header.IndexOf('<'); + if (index == -1) + { + return header; + } + index++; + var endIndex = header.IndexOf('>', index); + if (endIndex == -1) + { + return header; + } + var link = header.AsSpan(index, endIndex - index).ToString(); + var segments = link.Split('/'); + for (var j = 0; j < segments.Length; j++) + { + segments[j] = System.Net.WebUtility.UrlEncode(segments[j]); + } + var encoded = string.Join("/", segments); + return $"{header.Substring(0, index)}{encoded}{header.Substring(endIndex)}"; + } + private static (string, string[]) ParseAndSortPatterns(string patterns) { if (string.IsNullOrEmpty(patterns))