diff --git a/tests/SkiaSharp.Extended.UI.Maui.Tests/Controls/Lottie/SKLottieViewExtensions.cs b/tests/SkiaSharp.Extended.UI.Maui.Tests/Controls/Lottie/SKLottieViewExtensions.cs index a48cb03093..a22463d2d2 100644 --- a/tests/SkiaSharp.Extended.UI.Maui.Tests/Controls/Lottie/SKLottieViewExtensions.cs +++ b/tests/SkiaSharp.Extended.UI.Maui.Tests/Controls/Lottie/SKLottieViewExtensions.cs @@ -22,10 +22,13 @@ void OnAnimationLoaded(object? sender, EventArgs e) tcs.SetResult(true); } - void OnAnimationFailed(object? sender, EventArgs e) + void OnAnimationFailed(object? sender, SKLottieAnimationFailedEventArgs e) { Cleanup(); - tcs.SetException(new Exception("Unable to load Lottie animation.")); + if (e.Exception != null) + tcs.SetException(new Exception("Unable to load Lottie animation.", e.Exception)); + else + tcs.SetException(new Exception("Unable to load Lottie animation (animation source returned null).")); } void OnTimeout() diff --git a/tests/SkiaSharp.Extended.UI.Maui.Tests/Controls/Lottie/SKLottieViewTest.cs b/tests/SkiaSharp.Extended.UI.Maui.Tests/Controls/Lottie/SKLottieViewTest.cs index 5e7b8bc343..58d26fe7cb 100644 --- a/tests/SkiaSharp.Extended.UI.Maui.Tests/Controls/Lottie/SKLottieViewTest.cs +++ b/tests/SkiaSharp.Extended.UI.Maui.Tests/Controls/Lottie/SKLottieViewTest.cs @@ -147,6 +147,75 @@ public async Task NegativeUpdatesAfterPositiveGoesBack() Assert.Equal(0, animationCompleted); } + [Fact] + public async Task AnimationFailedContainsException() + { + // create - use non-existent file to trigger actual load failure with exception + var source = new SKFileLottieImageSource { File = "nonexistent.json" }; + var lottie = new SKLottieView { Source = source }; + + SKLottieAnimationFailedEventArgs? failedEventArgs = null; + var tcs = new TaskCompletionSource(); + + lottie.AnimationFailed += (s, e) => + { + failedEventArgs = e; + tcs.SetResult(true); + }; + + // wait for animation to fail (using consistent 3000ms timeout) + var completedTask = await Task.WhenAny(tcs.Task, Task.Delay(3000)); + + // test - verify the failure event was triggered with proper event args + Assert.Equal(tcs.Task, completedTask); + Assert.NotNull(failedEventArgs); + Assert.IsType(failedEventArgs); + // Verify exception is populated when file doesn't exist + Assert.NotNull(failedEventArgs.Exception); + } + + [Fact] + public async Task AnimationFailedNotTriggeredForEmptySource() + { + // create - empty source should NOT trigger AnimationFailed (early return) + var source = new SKFileLottieImageSource(); + var lottie = new SKLottieView { Source = source }; + + var failedTriggered = false; + var loadedTriggered = false; + + lottie.AnimationFailed += (s, e) => failedTriggered = true; + lottie.AnimationLoaded += (s, e) => loadedTriggered = true; + + // wait to ensure events have time to fire (using consistent 3000ms) + await Task.Delay(3000); + + // test - verify neither event was triggered for empty source + Assert.False(failedTriggered); + Assert.False(loadedTriggered); + } + + [Fact] + public async Task AnimationLoadedNotTriggeredOnFailure() + { + // create - non-existent file to trigger failure + var source = new SKFileLottieImageSource { File = "nonexistent.json" }; + var lottie = new SKLottieView { Source = source }; + + var loadedTriggered = false; + var tcs = new TaskCompletionSource(); + + lottie.AnimationLoaded += (s, e) => loadedTriggered = true; + lottie.AnimationFailed += (s, e) => tcs.SetResult(true); + + // wait for failure + var completedTask = await Task.WhenAny(tcs.Task, Task.Delay(3000)); + + // test - verify AnimationLoaded was NOT triggered on failure + Assert.Equal(tcs.Task, completedTask); + Assert.False(loadedTriggered); + } + [Theory] [InlineData(SKLottieRepeatMode.Restart, 1, 1)] [InlineData(SKLottieRepeatMode.Restart, 2, 2)]