diff --git a/CHANGELOG.md b/CHANGELOG.md index 0103cf05e9..cc914b94a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixes - Unknown stack frames in profiles on .NET 8+ ([#3967](https://github.com/getsentry/sentry-dotnet/pull/3967)) +- Missing MAUI `Shell` navigation breadcrumbs on iOS ([#4006](https://github.com/getsentry/sentry-dotnet/pull/4006)) ## 5.3.0 diff --git a/src/Sentry.Maui/Internal/MauiEventsBinder.cs b/src/Sentry.Maui/Internal/MauiEventsBinder.cs index 23605ccb2e..40356438b5 100644 --- a/src/Sentry.Maui/Internal/MauiEventsBinder.cs +++ b/src/Sentry.Maui/Internal/MauiEventsBinder.cs @@ -34,7 +34,16 @@ public void HandleApplicationEvents(Application application, bool bind = true) { if (bind) { - // Attach element events to all descendents as they are added to the application. + // Attach element events to all existing descendants (skip the application itself) + foreach (var descendant in application.GetVisualTreeDescendants().Skip(1)) + { + if (descendant is VisualElement element) + { + OnApplicationOnDescendantAdded(application, new ElementEventArgs(element)); + } + } + + // Attach element events to all descendants as they are added to the application. application.DescendantAdded += OnApplicationOnDescendantAdded; application.DescendantRemoved += OnApplicationOnDescendantRemoved; diff --git a/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs b/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs index 8a3c944f85..e691784afa 100644 --- a/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs +++ b/test/Sentry.EntityFramework.Tests/IntegrationTests.verify.cs @@ -5,7 +5,7 @@ namespace Sentry.EntityFramework.Tests; public class IntegrationTests { // needs to be a variable to stop EF from inlining it as a constant - static string shouldNotAppearInPayload = "SHOULD NOT APPEAR IN PAYLOAD"; + private static string shouldNotAppearInPayload = "SHOULD NOT APPEAR IN PAYLOAD"; [SkippableFact] public async Task Simple() diff --git a/test/Sentry.Maui.Tests/MauiEventsBinderTests.Application.cs b/test/Sentry.Maui.Tests/MauiEventsBinderTests.Application.cs index 81e2cb4f0d..3d659faaaf 100644 --- a/test/Sentry.Maui.Tests/MauiEventsBinderTests.Application.cs +++ b/test/Sentry.Maui.Tests/MauiEventsBinderTests.Application.cs @@ -145,6 +145,36 @@ public void Application_UnbindModalEvents_DoesNotAddBreadcrumb(string eventName, Assert.Single(_fixture.Scope.Breadcrumbs); } + [Fact] + public void Application_HandleApplicationEvents_TracksExistingDescendants() + { + // Arrange + var application = MockApplication.Create(); + var element = new MockVisualElement("element"); + var mainPage = new ContentPage + { + Content = new VerticalStackLayout + { + element + } + }; + + application.AddWindow(mainPage); + + _fixture.Binder.HandleApplicationEvents(application); + + // Act + element.RaiseEvent(nameof(VisualElement.Focused), new FocusEventArgs(element, true)); + + // Assert + var crumb = Assert.Single(_fixture.Scope.Breadcrumbs); + Assert.Equal($"{nameof(MockVisualElement)}.{nameof(VisualElement.Focused)}", crumb.Message); + Assert.Equal(BreadcrumbLevel.Info, crumb.Level); + Assert.Equal(MauiEventsBinder.SystemType, crumb.Type); + Assert.Equal(MauiEventsBinder.RenderingCategory, crumb.Category); + crumb.Data.Should().Contain($"{nameof(MockVisualElement)}.Name", "element"); + } + public static IEnumerable ApplicationModalEventsData { get diff --git a/test/Sentry.Maui.Tests/Mocks/MockApplication.cs b/test/Sentry.Maui.Tests/Mocks/MockApplication.cs index f1bf30ae20..e67d2405d7 100644 --- a/test/Sentry.Maui.Tests/Mocks/MockApplication.cs +++ b/test/Sentry.Maui.Tests/Mocks/MockApplication.cs @@ -5,6 +5,7 @@ namespace Sentry.Maui.Tests.Mocks; public class MockApplication : Application { private static readonly object LockObj = new(); + private Page _mainPage; static MockApplication() { @@ -18,6 +19,23 @@ private MockApplication() { } + public void AddWindow(Page mainPage) + { + _mainPage = mainPage; + ((IApplication)this).CreateWindow(null); + _mainPage = null; + } + + protected override Window CreateWindow(IActivationState activationState) + { + if (_mainPage != null) + { + return new Window(_mainPage); + } + + return base.CreateWindow(activationState); + } + public static MockApplication Create() { // The base constructor will try to set the mock as the current application, which we don't want in tests. diff --git a/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/AppDelegate.cs b/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/AppDelegate.cs index e24cb88f7b..b1699db753 100644 --- a/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/AppDelegate.cs +++ b/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/AppDelegate.cs @@ -1,9 +1,9 @@ -using Foundation; +using Foundation; namespace Sentry.MauiTrimTest; [Register("AppDelegate")] public class AppDelegate : MauiUIApplicationDelegate { - protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } diff --git a/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs b/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs index 05a59280ab..41d708b660 100644 --- a/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs +++ b/test/Sentry.MauiTrimTest/Platforms/MacCatalyst/Program.cs @@ -5,11 +5,11 @@ namespace Sentry.MauiTrimTest; public class Program { - // This is the main entry point of the application. - static void Main(string[] args) - { - // if you want to use a different Application Delegate class from "AppDelegate" - // you can specify it here. - UIApplication.Main(args, null, typeof(AppDelegate)); - } + // This is the main entry point of the application. + private static void Main(string[] args) + { + // if you want to use a different Application Delegate class from "AppDelegate" + // you can specify it here. + UIApplication.Main(args, null, typeof(AppDelegate)); + } } diff --git a/test/Sentry.MauiTrimTest/Platforms/Tizen/Main.cs b/test/Sentry.MauiTrimTest/Platforms/Tizen/Main.cs index d49b5550af..7c820fbe05 100644 --- a/test/Sentry.MauiTrimTest/Platforms/Tizen/Main.cs +++ b/test/Sentry.MauiTrimTest/Platforms/Tizen/Main.cs @@ -4,13 +4,13 @@ namespace Sentry.MauiTrimTest; -class Program : MauiApplication +public class Program : MauiApplication { - protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); - static void Main(string[] args) - { - var app = new Program(); - app.Run(args); - } + private static void Main(string[] args) + { + var app = new Program(); + app.Run(args); + } } diff --git a/test/Sentry.MauiTrimTest/Platforms/Windows/App.xaml.cs b/test/Sentry.MauiTrimTest/Platforms/Windows/App.xaml.cs index dbacb63fe1..5f852b91c0 100644 --- a/test/Sentry.MauiTrimTest/Platforms/Windows/App.xaml.cs +++ b/test/Sentry.MauiTrimTest/Platforms/Windows/App.xaml.cs @@ -1,4 +1,4 @@ -using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -10,15 +10,15 @@ namespace Sentry.MauiTrimTest.WinUI; /// public partial class App : MauiWinUIApplication { - /// - /// Initializes the singleton application object. This is the first line of authored code - /// executed, and as such is the logical equivalent of main() or WinMain(). - /// - public App() - { - this.InitializeComponent(); - } + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + } - protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); }