diff --git a/.generated.NoMobile.sln b/.generated.NoMobile.sln index 1301310a3e..df9a68c619 100644 --- a/.generated.NoMobile.sln +++ b/.generated.NoMobile.sln @@ -278,6 +278,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "integration-test", "integra integration-test\pester.ps1 = integration-test\pester.ps1 integration-test\ios.Tests.ps1 = integration-test\ios.Tests.ps1 integration-test\msbuild.Tests.ps1 = integration-test\msbuild.Tests.ps1 + integration-test\android.Tests.ps1 = integration-test\android.Tests.ps1 EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net4-console", "net4-console", "{33793113-C7B5-434D-B5C1-6CA1A9587842}" @@ -290,6 +291,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Extensions.AI", "src EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Extensions.AI.Tests", "test\Sentry.Extensions.AI.Tests\Sentry.Extensions.AI.Tests.csproj", "{28D6E004-DC64-464F-91BE-BC0B3A8E543F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AspNetCore.Blazor.WebAssembly.Tests", "test\Sentry.AspNetCore.Blazor.WebAssembly.Tests\Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj", "{705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1357,6 +1360,18 @@ Global {28D6E004-DC64-464F-91BE-BC0B3A8E543F}.Release|x64.Build.0 = Release|Any CPU {28D6E004-DC64-464F-91BE-BC0B3A8E543F}.Release|x86.ActiveCfg = Release|Any CPU {28D6E004-DC64-464F-91BE-BC0B3A8E543F}.Release|x86.Build.0 = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|x64.ActiveCfg = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|x64.Build.0 = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|x86.ActiveCfg = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|x86.Build.0 = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|Any CPU.Build.0 = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|x64.ActiveCfg = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|x64.Build.0 = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|x86.ActiveCfg = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1457,5 +1472,6 @@ Global {3D91128A-5695-4BAE-B939-5F5F7C56A679} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {AE461926-00B8-4FDD-A41D-3C83C2D9A045} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {28D6E004-DC64-464F-91BE-BC0B3A8E543F} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} EndGlobalSection EndGlobal diff --git a/CHANGELOG.md b/CHANGELOG.md index 396ee4a7a8..f7c0db7d39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Add navigation breadcrumbs for Blazor WebAssembly ([#4907](https://github.com/getsentry/sentry-dotnet/pull/4907)) - Add _experimental_ support for [Sentry trace-connected Metrics](https://docs.sentry.io/product/explore/metrics/) ([#4834](https://github.com/getsentry/sentry-dotnet/pull/4834)) - Extended `SentryThread` by `Main` to allow indication whether the thread is considered the current main thread ([#4807](https://github.com/getsentry/sentry-dotnet/pull/4807)) diff --git a/Sentry-CI-Build-Linux-NoMobile.slnf b/Sentry-CI-Build-Linux-NoMobile.slnf index a0bba5a7b5..5aff2b9e87 100644 --- a/Sentry-CI-Build-Linux-NoMobile.slnf +++ b/Sentry-CI-Build-Linux-NoMobile.slnf @@ -49,6 +49,7 @@ "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", + "test\\Sentry.AspNetCore.Blazor.WebAssembly.Tests\\Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", "test\\Sentry.AspNetCore.TestUtils\\Sentry.AspNetCore.TestUtils.csproj", diff --git a/Sentry-CI-Build-Linux.slnf b/Sentry-CI-Build-Linux.slnf index c3cb98016e..dd182881d2 100644 --- a/Sentry-CI-Build-Linux.slnf +++ b/Sentry-CI-Build-Linux.slnf @@ -56,6 +56,7 @@ "src\\Sentry\\Sentry.csproj", "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", + "test\\Sentry.AspNetCore.Blazor.WebAssembly.Tests\\Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", "test\\Sentry.AspNetCore.TestUtils\\Sentry.AspNetCore.TestUtils.csproj", diff --git a/Sentry-CI-Build-Windows-arm64.slnf b/Sentry-CI-Build-Windows-arm64.slnf index 9ee5420055..5878f20b2f 100644 --- a/Sentry-CI-Build-Windows-arm64.slnf +++ b/Sentry-CI-Build-Windows-arm64.slnf @@ -58,6 +58,7 @@ "src\\Sentry\\Sentry.csproj", "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", + "test\\Sentry.AspNetCore.Blazor.WebAssembly.Tests\\Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", "test\\Sentry.AspNetCore.TestUtils\\Sentry.AspNetCore.TestUtils.csproj", diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf index 46f5fd978f..6bc68aeaad 100644 --- a/Sentry-CI-Build-Windows.slnf +++ b/Sentry-CI-Build-Windows.slnf @@ -58,6 +58,7 @@ "src\\Sentry\\Sentry.csproj", "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", + "test\\Sentry.AspNetCore.Blazor.WebAssembly.Tests\\Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", "test\\Sentry.AspNetCore.TestUtils\\Sentry.AspNetCore.TestUtils.csproj", diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf index ab4d5302ab..7a024d3b5e 100644 --- a/Sentry-CI-Build-macOS.slnf +++ b/Sentry-CI-Build-macOS.slnf @@ -63,6 +63,7 @@ "src\\Sentry\\Sentry.csproj", "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", + "test\\Sentry.AspNetCore.Blazor.WebAssembly.Tests\\Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", "test\\Sentry.AspNetCore.TestUtils\\Sentry.AspNetCore.TestUtils.csproj", diff --git a/Sentry.sln b/Sentry.sln index f6db5afa73..df9a68c619 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -291,6 +291,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Extensions.AI", "src EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Extensions.AI.Tests", "test\Sentry.Extensions.AI.Tests\Sentry.Extensions.AI.Tests.csproj", "{28D6E004-DC64-464F-91BE-BC0B3A8E543F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.AspNetCore.Blazor.WebAssembly.Tests", "test\Sentry.AspNetCore.Blazor.WebAssembly.Tests\Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj", "{705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1358,6 +1360,18 @@ Global {28D6E004-DC64-464F-91BE-BC0B3A8E543F}.Release|x64.Build.0 = Release|Any CPU {28D6E004-DC64-464F-91BE-BC0B3A8E543F}.Release|x86.ActiveCfg = Release|Any CPU {28D6E004-DC64-464F-91BE-BC0B3A8E543F}.Release|x86.Build.0 = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|x64.ActiveCfg = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|x64.Build.0 = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|x86.ActiveCfg = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Debug|x86.Build.0 = Debug|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|Any CPU.Build.0 = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|x64.ActiveCfg = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|x64.Build.0 = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|x86.ActiveCfg = Release|Any CPU + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1458,5 +1472,6 @@ Global {3D91128A-5695-4BAE-B939-5F5F7C56A679} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {AE461926-00B8-4FDD-A41D-3C83C2D9A045} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} {28D6E004-DC64-464F-91BE-BC0B3A8E543F} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} + {705223D7-FEE3-485B-8DFB-3F9B29DAC6CA} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} EndGlobalSection EndGlobal diff --git a/SentryAspNetCore.slnf b/SentryAspNetCore.slnf index 7b89d9e91c..c5eea865ea 100644 --- a/SentryAspNetCore.slnf +++ b/SentryAspNetCore.slnf @@ -24,6 +24,7 @@ "src\\Sentry.Serilog\\Sentry.Serilog.csproj", "src\\Sentry\\Sentry.csproj", "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", + "test\\Sentry.AspNetCore.Blazor.WebAssembly.Tests\\Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", "test\\Sentry.AspNetCore.TestUtils\\Sentry.AspNetCore.TestUtils.csproj", diff --git a/SentryNoMobile.slnf b/SentryNoMobile.slnf index e4754f322d..e4e8cd9771 100644 --- a/SentryNoMobile.slnf +++ b/SentryNoMobile.slnf @@ -52,6 +52,7 @@ "src\\Sentry\\Sentry.csproj", "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", + "test\\Sentry.AspNetCore.Blazor.WebAssembly.Tests\\Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", "test\\Sentry.AspNetCore.TestUtils\\Sentry.AspNetCore.TestUtils.csproj", diff --git a/SentryNoSamples.slnf b/SentryNoSamples.slnf index d6231a7e06..2fa3d0bf7a 100644 --- a/SentryNoSamples.slnf +++ b/SentryNoSamples.slnf @@ -27,6 +27,7 @@ "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", + "test\\Sentry.AspNetCore.Blazor.WebAssembly.Tests\\Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", "test\\Sentry.AspNetCore.TestUtils\\Sentry.AspNetCore.TestUtils.csproj", diff --git a/src/Sentry.AspNetCore.Blazor.WebAssembly/Internal/BlazorWasmOptionsSetup.cs b/src/Sentry.AspNetCore.Blazor.WebAssembly/Internal/BlazorWasmOptionsSetup.cs new file mode 100644 index 0000000000..ad1f5bbcc9 --- /dev/null +++ b/src/Sentry.AspNetCore.Blazor.WebAssembly/Internal/BlazorWasmOptionsSetup.cs @@ -0,0 +1,72 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.Extensions.Options; +using Sentry.Extensibility; + +namespace Sentry.AspNetCore.Blazor.WebAssembly.Internal; + +internal sealed class BlazorWasmOptionsSetup : IConfigureOptions +{ + private readonly NavigationManager _navigationManager; + private readonly IHub _hub; + private bool _initialized; + + public BlazorWasmOptionsSetup(NavigationManager navigationManager) + : this(navigationManager, HubAdapter.Instance) + { + } + + internal BlazorWasmOptionsSetup(NavigationManager navigationManager, IHub hub) + { + _navigationManager = navigationManager; + _hub = hub; + } + + public void Configure(SentryBlazorOptions options) + { + ArgumentNullException.ThrowIfNull(options); + + if (_initialized) + { + return; + } + _initialized = true; + + var previousUrl = _navigationManager.Uri; + + // Set the initial scope request URL + _hub.ConfigureScope(scope => + { + scope.Request.Url = ToRelativePath(previousUrl); + }); + + _navigationManager.LocationChanged += (_, args) => + { + var from = ToRelativePath(previousUrl); + var to = ToRelativePath(args.Location); + + _hub.AddBreadcrumb( + new Breadcrumb( + type: "navigation", + category: "navigation", + data: new Dictionary + { + { "from", from }, + { "to", to } + })); + + _hub.ConfigureScope(scope => + { + scope.Request.Url = to; + }); + + previousUrl = args.Location; + }; + } + + private string ToRelativePath(string url) + { + var relative = _navigationManager.ToBaseRelativePath(url); + return "/" + relative; + } +} diff --git a/src/Sentry.AspNetCore.Blazor.WebAssembly/Sentry.AspNetCore.Blazor.WebAssembly.csproj b/src/Sentry.AspNetCore.Blazor.WebAssembly/Sentry.AspNetCore.Blazor.WebAssembly.csproj index b0635091c6..c02680154d 100644 --- a/src/Sentry.AspNetCore.Blazor.WebAssembly/Sentry.AspNetCore.Blazor.WebAssembly.csproj +++ b/src/Sentry.AspNetCore.Blazor.WebAssembly/Sentry.AspNetCore.Blazor.WebAssembly.csproj @@ -10,6 +10,10 @@ + + + + diff --git a/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs b/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs index ca208e0280..8985e9cc5e 100644 --- a/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs +++ b/src/Sentry.AspNetCore.Blazor.WebAssembly/WebAssemblyHostBuilderExtensions.cs @@ -1,5 +1,8 @@ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Sentry; +using Sentry.AspNetCore.Blazor.WebAssembly.Internal; using Sentry.Extensions.Logging; // ReSharper disable once CheckNamespace - Discoverability @@ -30,6 +33,8 @@ public static WebAssemblyHostBuilder UseSentry(this WebAssemblyHostBuilder build blazorOptions.IsGlobalModeEnabled = true; }); + builder.Services.AddSingleton, BlazorWasmOptionsSetup>(); + return builder; } } diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index a2b75db0ae..2fce203ef5 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -137,6 +137,7 @@ + diff --git a/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/BlazorWasmOptionsSetupTests.cs b/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/BlazorWasmOptionsSetupTests.cs new file mode 100644 index 0000000000..d9960f3087 --- /dev/null +++ b/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/BlazorWasmOptionsSetupTests.cs @@ -0,0 +1,150 @@ +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Sentry.AspNetCore.Blazor.WebAssembly.Internal; + +namespace Sentry.AspNetCore.Blazor.WebAssembly.Tests; + +public class BlazorWasmOptionsSetupTests +{ + private readonly FakeNavigationManager _navigationManager; + private readonly IHub _hub; + private readonly Scope _scope; + private readonly BlazorWasmOptionsSetup _sut; + + public BlazorWasmOptionsSetupTests() + { + _navigationManager = new FakeNavigationManager( + baseUri: "https://localhost:5001/", + initialUri: "https://localhost:5001/"); + + _hub = Substitute.For(); + _scope = new Scope(new SentryOptions()); + _hub.SubstituteConfigureScope(_scope); + + _sut = new BlazorWasmOptionsSetup(_navigationManager, _hub); + } + + [Fact] + public void Configure_SetsInitialRequestUrl() + { + // Act + _sut.Configure(new SentryBlazorOptions()); + + // Assert + _scope.Request.Url.Should().Be("/"); + } + + [Fact] + public void Configure_SetsInitialRequestUrl_WithPath() + { + // Arrange + var nav = new FakeNavigationManager( + baseUri: "https://localhost:5001/", + initialUri: "https://localhost:5001/counter"); + var sut = new BlazorWasmOptionsSetup(nav, _hub); + + // Act + sut.Configure(new SentryBlazorOptions()); + + // Assert + _scope.Request.Url.Should().Be("/counter"); + } + + [Fact] + public void Navigation_CreatesBreadcrumbWithCorrectTypeAndCategory() + { + // Arrange + _sut.Configure(new SentryBlazorOptions()); + + // Act + _navigationManager.NavigateTo("/dashboard"); + + // Assert + var crumb = _scope.Breadcrumbs.Should().ContainSingle().Subject; + crumb.Type.Should().Be("navigation"); + crumb.Category.Should().Be("navigation"); + } + + [Fact] + public void Navigation_BreadcrumbHasNoMessage() + { + // Arrange + _sut.Configure(new SentryBlazorOptions()); + + // Act + _navigationManager.NavigateTo("/dashboard"); + + // Assert + var crumb = _scope.Breadcrumbs.Should().ContainSingle().Subject; + crumb.Message.Should().BeNull(); + } + + [Fact] + public void Navigation_CreatesBreadcrumbWithRelativePaths() + { + // Arrange + _sut.Configure(new SentryBlazorOptions()); + + // Act + _navigationManager.NavigateTo("/dashboard"); + + // Assert + var crumb = _scope.Breadcrumbs.Should().ContainSingle().Subject; + crumb.Data.Should().ContainKey("from").WhoseValue.Should().Be("/"); + crumb.Data.Should().ContainKey("to").WhoseValue.Should().Be("/dashboard"); + } + + [Fact] + public void Navigation_UpdatesRequestUrl() + { + // Arrange + _sut.Configure(new SentryBlazorOptions()); + + // Act + _navigationManager.NavigateTo("/dashboard"); + + // Assert + _scope.Request.Url.Should().Be("/dashboard"); + } + + [Fact] + public void MultipleNavigations_TrackFromCorrectly() + { + // Arrange + _sut.Configure(new SentryBlazorOptions()); + + // Act + _navigationManager.NavigateTo("/page1"); + _navigationManager.NavigateTo("/page2"); + + // Assert + var breadcrumbs = _scope.Breadcrumbs.ToList(); + breadcrumbs.Should().HaveCount(2); + + var first = breadcrumbs[0]; + first.Data.Should().ContainKey("from").WhoseValue.Should().Be("/"); + first.Data.Should().ContainKey("to").WhoseValue.Should().Be("/page1"); + + var second = breadcrumbs[1]; + second.Data.Should().ContainKey("from").WhoseValue.Should().Be("/page1"); + second.Data.Should().ContainKey("to").WhoseValue.Should().Be("/page2"); + } + + [Fact] + public void Navigation_FromInitialPath_TracksCorrectFrom() + { + // Arrange - start on /login + var nav = new FakeNavigationManager( + baseUri: "https://localhost:5001/", + initialUri: "https://localhost:5001/login"); + var sut = new BlazorWasmOptionsSetup(nav, _hub); + sut.Configure(new SentryBlazorOptions()); + + // Act + nav.NavigateTo("/home"); + + // Assert + var crumb = _scope.Breadcrumbs.Should().ContainSingle().Subject; + crumb.Data.Should().ContainKey("from").WhoseValue.Should().Be("/login"); + crumb.Data.Should().ContainKey("to").WhoseValue.Should().Be("/home"); + } +} diff --git a/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/FakeNavigationManager.cs b/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/FakeNavigationManager.cs new file mode 100644 index 0000000000..e0fddc9587 --- /dev/null +++ b/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/FakeNavigationManager.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Components; + +namespace Sentry.AspNetCore.Blazor.WebAssembly.Tests; + +internal sealed class FakeNavigationManager : NavigationManager +{ + public FakeNavigationManager(string baseUri = "https://localhost/", string initialUri = "https://localhost/") + { + Initialize(baseUri, initialUri); + } + + protected override void NavigateToCore(string uri, bool forceLoad) + { + var absoluteUri = ToAbsoluteUri(uri).ToString(); + Uri = absoluteUri; + NotifyLocationChanged(isInterceptedLink: false); + } +} diff --git a/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj b/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj new file mode 100644 index 0000000000..d65aec285a --- /dev/null +++ b/test/Sentry.AspNetCore.Blazor.WebAssembly.Tests/Sentry.AspNetCore.Blazor.WebAssembly.Tests.csproj @@ -0,0 +1,12 @@ + + + + $(CurrentTfms) + + + + + + + + diff --git a/test/Sentry.Testing/Sentry.Testing.csproj b/test/Sentry.Testing/Sentry.Testing.csproj index 4a5c86e2bd..66a5feab07 100644 --- a/test/Sentry.Testing/Sentry.Testing.csproj +++ b/test/Sentry.Testing/Sentry.Testing.csproj @@ -18,6 +18,7 @@ +