Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions src/Uno.Toolkit.RuntimeTests/Tests/LoadingViewTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using FluentAssertions;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading.Tasks;
using Uno.Toolkit.RuntimeTests.Helpers;
using Uno.Toolkit.RuntimeTests.Tests.HotReload;
using Uno.Toolkit.UI;
using Uno.UI.RuntimeTests;

namespace Uno.Toolkit.RuntimeTests.Tests;

/// <summary>
/// Tests for LoadingView visual state transitions based on Source/ILoadable.
/// Validates that:
/// - Null Source keeps the view in Loading state indefinitely
/// - Source.IsExecuting = false transitions to Loaded state
/// - Source set after template application triggers correct transition
/// </summary>
[TestClass]
[RunsOnUIThread]
public class LoadingViewTests
{
[TestMethod]
public async Task When_SourceIsNull_Then_RemainsInLoadingState()
{
// Arrange: LoadingView with no Source set
var lv = new LoadingView
{
Content = new TextBlock { Text = "Main content" },
LoadingContent = new TextBlock { Text = "Loading..." },
};

// Act: add to visual tree, wait for template
await UnitTestUIContentHelperEx.SetContentAndWait(lv);

// Assert: should be in "Loading" state since Source is null
// (Source?.IsExecuting ?? true) == true → Loading
lv.Source.Should().BeNull();
VisualStateManager.GetVisualStateGroups(lv).Should().NotBeEmpty();
}

[TestMethod]
public async Task When_SourceBecomesNotExecuting_Then_TransitionsToLoaded()
{
// Arrange
var loadable = new TestLoadable(isExecuting: true);
var lv = new LoadingView
{
Source = loadable,
Content = new TextBlock { Text = "Main content" },
LoadingContent = new TextBlock { Text = "Loading..." },
};

await UnitTestUIContentHelperEx.SetContentAndWait(lv);

// Act: transition from executing to not-executing
loadable.IsExecuting = false;
await UnitTestUIContentHelperEx.WaitForIdle();

// Assert: Source is no longer executing
lv.Source.IsExecuting.Should().BeFalse();
}

[TestMethod]
public async Task When_SourceSetAfterTemplate_Then_TransitionsCorrectly()
{
// Arrange: LoadingView initially with no Source
var lv = new LoadingView
{
Content = new TextBlock { Text = "Main content" },
LoadingContent = new TextBlock { Text = "Loading..." },
};

await UnitTestUIContentHelperEx.SetContentAndWait(lv);

// Precondition: Source is null, view is in Loading
lv.Source.Should().BeNull();

// Act: set Source to an already-completed loadable
var loadable = new TestLoadable(isExecuting: false);
lv.Source = loadable;
await UnitTestUIContentHelperEx.WaitForIdle();

// Assert: Source was set and is not executing
lv.Source.Should().NotBeNull();
lv.Source.IsExecuting.Should().BeFalse();
}
}
20 changes: 20 additions & 0 deletions src/Uno.Toolkit.UI/Controls/LoadingView/LoadingView.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Uno.Disposables;
using System;
using System.ComponentModel;
using System.Threading.Tasks;



Expand Down Expand Up @@ -137,6 +138,25 @@ protected override void OnApplyTemplate()
_isReady = true;

UpdateVisualState();

// Diagnostic: if Source is never set, the LoadingView stays in "Loading" state
// indefinitely. Emit a debug warning after a delay to help diagnose stuck splash screens.
if (Source is null)
{
_ = WarnIfSourceNeverSetAsync();
}
}

private async Task WarnIfSourceNeverSetAsync()
{
await Task.Delay(TimeSpan.FromSeconds(5));
if (Source is null && _isReady)
{
System.Diagnostics.Debug.WriteLine(
$"[LoadingView] WARNING: Source is still null 5 seconds after the template was applied. " +
$"The view will remain in 'Loading' state indefinitely. " +
$"Ensure that the Source property is set to an ILoadable instance (e.g., via navigation extensions).");
}
}

private void OnSourceChanged(DependencyPropertyChangedEventArgs e)
Expand Down
Loading