|  | 
| 1 | 1 | // Licensed to the .NET Foundation under one or more agreements. | 
| 2 | 2 | // The .NET Foundation licenses this file to you under the MIT license. | 
| 3 | 3 | 
 | 
|  | 4 | +using System.Text.Json; | 
| 4 | 5 | using Microsoft.AspNetCore.StaticWebAssets.Tasks; | 
| 5 | 6 | using Microsoft.NET.Sdk.StaticWebAssets.Tasks; | 
| 6 |  | -using NuGet.Packaging; | 
| 7 | 7 | 
 | 
| 8 | 8 | namespace Microsoft.NET.Sdk.Razor.Tests; | 
| 9 | 9 | 
 | 
| @@ -242,4 +242,174 @@ public void Publish_CreatesEndpointsForAssets_BuildAndPublish_Assets() | 
| 242 | 242 | 
 | 
| 243 | 243 |         AssertManifest(publishManifest, LoadPublishManifest()); | 
| 244 | 244 |     } | 
|  | 245 | + | 
|  | 246 | +    [Fact] | 
|  | 247 | +    public void Build_EndpointManifest_ContainsEndpoints() | 
|  | 248 | +    { | 
|  | 249 | +        // Arrange | 
|  | 250 | +        var expectedExtensions = new[] { ".pdb", ".js", ".wasm" }; | 
|  | 251 | +        var testAppName = "BlazorWasmWithLibrary"; | 
|  | 252 | +        var testInstance = CreateAspNetSdkTestAsset(testAppName) | 
|  | 253 | +            .WithProjectChanges((p, doc) => | 
|  | 254 | +            { | 
|  | 255 | +                if (Path.GetFileName(p) == "blazorwasm.csproj") | 
|  | 256 | +                { | 
|  | 257 | +                    var itemGroup = new XElement("PropertyGroup"); | 
|  | 258 | +                    var serviceWorkerAssetsManifest = new XElement("ServiceWorkerAssetsManifest", "service-worker-assets.js"); | 
|  | 259 | +                    itemGroup.Add(serviceWorkerAssetsManifest); | 
|  | 260 | +                    doc.Root.Add(itemGroup); | 
|  | 261 | +                } | 
|  | 262 | +            }); | 
|  | 263 | + | 
|  | 264 | +        var buildCommand = new BuildCommand(testInstance, "blazorwasm"); | 
|  | 265 | +        buildCommand.WithWorkingDirectory(testInstance.TestRoot); | 
|  | 266 | +        buildCommand.Execute("/bl").Should().Pass(); | 
|  | 267 | + | 
|  | 268 | +        var buildOutputDirectory = buildCommand.GetOutputDirectory(DefaultTfm).ToString(); | 
|  | 269 | +        VerifyEndpointsCollection(buildOutputDirectory, "blazorwasm", readFromDevManifest: true); | 
|  | 270 | +    } | 
|  | 271 | + | 
|  | 272 | +    [Fact] | 
|  | 273 | +    public void BuildHosted_EndpointManifest_ContainsEndpoints() | 
|  | 274 | +    { | 
|  | 275 | +        // Arrange | 
|  | 276 | +        var testAppName = "BlazorHosted"; | 
|  | 277 | +        var testInstance = CreateAspNetSdkTestAsset(testAppName); | 
|  | 278 | + | 
|  | 279 | +        var buildCommand = new BuildCommand(testInstance, "blazorhosted"); | 
|  | 280 | +        buildCommand.Execute() | 
|  | 281 | +            .Should().Pass(); | 
|  | 282 | + | 
|  | 283 | +        var buildOutputDirectory = OutputPathCalculator.FromProject(Path.Combine(testInstance.TestRoot, "blazorhosted")).GetOutputDirectory(); | 
|  | 284 | + | 
|  | 285 | +        VerifyEndpointsCollection(buildOutputDirectory, "blazorhosted", readFromDevManifest: true); | 
|  | 286 | +    } | 
|  | 287 | + | 
|  | 288 | +    [Fact] | 
|  | 289 | +    public void Publish_EndpointManifestContainsEndpoints() | 
|  | 290 | +    { | 
|  | 291 | +        // Arrange | 
|  | 292 | +        var testAppName = "BlazorWasmWithLibrary"; | 
|  | 293 | +        var testInstance = CreateAspNetSdkTestAsset(testAppName); | 
|  | 294 | + | 
|  | 295 | +        var publishCommand = new PublishCommand(testInstance, "blazorwasm"); | 
|  | 296 | +        publishCommand.Execute().Should().Pass(); | 
|  | 297 | + | 
|  | 298 | +        var publishOutputDirectory = publishCommand.GetOutputDirectory(DefaultTfm).ToString(); | 
|  | 299 | + | 
|  | 300 | +        VerifyEndpointsCollection(publishOutputDirectory, "blazorwasm"); | 
|  | 301 | +    } | 
|  | 302 | + | 
|  | 303 | +    [Fact] | 
|  | 304 | +    public void PublishHosted_EndpointManifest_ContainsEndpoints() | 
|  | 305 | +    { | 
|  | 306 | +        // Arrange | 
|  | 307 | +        var testAppName = "BlazorHosted"; | 
|  | 308 | +        var testInstance = CreateAspNetSdkTestAsset(testAppName); | 
|  | 309 | + | 
|  | 310 | +        var publishCommand = new PublishCommand(testInstance, "blazorhosted"); | 
|  | 311 | +        publishCommand.Execute().Should().Pass(); | 
|  | 312 | + | 
|  | 313 | +        var publishOutputDirectory = publishCommand.GetOutputDirectory(DefaultTfm).ToString(); | 
|  | 314 | + | 
|  | 315 | +        VerifyEndpointsCollection(publishOutputDirectory, "blazorhosted"); | 
|  | 316 | +    } | 
|  | 317 | + | 
|  | 318 | +    // Makes several assertions about the endpoints we defined. | 
|  | 319 | +    // All assets have at least one endpoint. | 
|  | 320 | +    // No endpoint points to a non-existent asset | 
|  | 321 | +    // All compressed assets have 2 endpoints (one for the path with the extension, one for the path without the extension) | 
|  | 322 | +    // All uncompressed assets have 1 endpoint | 
|  | 323 | +    private static void VerifyEndpointsCollection(string outputDirectory, string projectName, bool readFromDevManifest = false) | 
|  | 324 | +    { | 
|  | 325 | +        var endpointsManifestFile = Path.Combine(outputDirectory, $"{projectName}.staticwebassets.endpoints.json"); | 
|  | 326 | + | 
|  | 327 | +        var endpoints = JsonSerializer.Deserialize<StaticWebAssetEndpointsManifest>(File.ReadAllText(endpointsManifestFile)); | 
|  | 328 | + | 
|  | 329 | +        var wwwrootFolderFiles = GetWwwrootFolderFiles(outputDirectory); | 
|  | 330 | + | 
|  | 331 | +        var foundAssets = new HashSet<string>(); | 
|  | 332 | +        var endpointsByAssetFile = endpoints.Endpoints.GroupBy(e => e.AssetFile).ToDictionary(g => g.Key, g => g.ToArray()); | 
|  | 333 | + | 
|  | 334 | +        foreach (var endpoint in endpoints.Endpoints) | 
|  | 335 | +        { | 
|  | 336 | +            wwwrootFolderFiles.Should().Contain(endpoint.AssetFile); | 
|  | 337 | +            foundAssets.Add(endpoint.AssetFile); | 
|  | 338 | +        } | 
|  | 339 | + | 
|  | 340 | +        wwwrootFolderFiles.Should().BeEquivalentTo(foundAssets); | 
|  | 341 | + | 
|  | 342 | +        foreach (var file in wwwrootFolderFiles) | 
|  | 343 | +        { | 
|  | 344 | +            endpointsByAssetFile.Should().ContainKey(file); | 
|  | 345 | +            if (file.EndsWith(".br") || file.EndsWith(".gz")) | 
|  | 346 | +            { | 
|  | 347 | +                endpointsByAssetFile[file].Should().HaveCount(2); | 
|  | 348 | +            } | 
|  | 349 | +            else | 
|  | 350 | +            { | 
|  | 351 | +                endpointsByAssetFile[file].Should().HaveCount(1); | 
|  | 352 | +            } | 
|  | 353 | +        } | 
|  | 354 | + | 
|  | 355 | +        HashSet<string> GetWwwrootFolderFiles(string outputDirectory) | 
|  | 356 | +        { | 
|  | 357 | +            if (!readFromDevManifest) | 
|  | 358 | +            { | 
|  | 359 | +                return new(Directory.GetFiles(Path.Combine(outputDirectory, "wwwroot"), "*", SearchOption.AllDirectories) | 
|  | 360 | +                        .Select(a => StaticWebAsset.Normalize(Path.GetRelativePath(Path.Combine(outputDirectory, "wwwroot"), a)))); | 
|  | 361 | +            } | 
|  | 362 | +            else | 
|  | 363 | +            { | 
|  | 364 | +                var staticWebAssetDevelopmentManifest = JsonSerializer.Deserialize<StaticWebAssetsDevelopmentManifest>(File.ReadAllText(Path.Combine(outputDirectory, $"{projectName}.staticwebassets.runtime.json"))); | 
|  | 365 | +                var endpoints = new HashSet<string>(); | 
|  | 366 | + | 
|  | 367 | +                //Traverse the node tree and compute the paths for all assets | 
|  | 368 | +                Traverse(staticWebAssetDevelopmentManifest.Root, "", endpoints); | 
|  | 369 | +                return endpoints; | 
|  | 370 | +            } | 
|  | 371 | +        } | 
|  | 372 | +    } | 
|  | 373 | + | 
|  | 374 | +    private static void Traverse(StaticWebAssetNode node, string pathSoFar, HashSet<string> endpoints) | 
|  | 375 | +    { | 
|  | 376 | +        if (node.Asset != null) | 
|  | 377 | +        { | 
|  | 378 | +            endpoints.Add(StaticWebAsset.Normalize(pathSoFar)); | 
|  | 379 | +        } | 
|  | 380 | +        else | 
|  | 381 | +        { | 
|  | 382 | +            foreach (var child in node.Children) | 
|  | 383 | +            { | 
|  | 384 | +                Traverse(child.Value, Path.Combine(pathSoFar, child.Key), endpoints); | 
|  | 385 | +            } | 
|  | 386 | +        } | 
|  | 387 | +    } | 
|  | 388 | + | 
|  | 389 | +    public class StaticWebAssetsDevelopmentManifest | 
|  | 390 | +    { | 
|  | 391 | +        public string[] ContentRoots { get; set; } | 
|  | 392 | + | 
|  | 393 | +        public StaticWebAssetNode Root { get; set; } | 
|  | 394 | +    } | 
|  | 395 | + | 
|  | 396 | +    public class StaticWebAssetPattern | 
|  | 397 | +    { | 
|  | 398 | +        public int ContentRootIndex { get; set; } | 
|  | 399 | +        public string Pattern { get; set; } | 
|  | 400 | +        public int Depth { get; set; } | 
|  | 401 | +    } | 
|  | 402 | + | 
|  | 403 | +    public class StaticWebAssetMatch | 
|  | 404 | +    { | 
|  | 405 | +        public int ContentRootIndex { get; set; } | 
|  | 406 | +        public string SubPath { get; set; } | 
|  | 407 | +    } | 
|  | 408 | + | 
|  | 409 | +    public class StaticWebAssetNode | 
|  | 410 | +    { | 
|  | 411 | +        public Dictionary<string, StaticWebAssetNode> Children { get; set; } | 
|  | 412 | +        public StaticWebAssetMatch Asset { get; set; } | 
|  | 413 | +        public StaticWebAssetPattern[] Patterns { get; set; } | 
|  | 414 | +    } | 
| 245 | 415 | } | 
0 commit comments