using Avalonia.Controls;
using DodoSSH.Client.App.Views;
namespace DodoSSH.Client.App.Layout.Tests;
///
/// The splash, and the one thing about it that can go wrong.
///
///
///
/// Three stacked elements in a fixed-width window is not a layout with much to get wrong — except in one
/// state. SplashWindow.CouldNotStart carries an exception's own message, whose length nothing in this
/// application decides, and that message is the whole reason that window is still on screen. Inside a fixed
/// height it would wrap and be clipped at exactly the wrong moment.
///
///
/// So what is measured here is that the message grows the card, and that the window is declared in a way
/// that lets the growth through. LayoutHarness.Unreachable is no help: it reports interactive controls
/// arranged where they cannot be used, and the splash has none — which is why these two assert on measured
/// heights instead.
///
///
public sealed class SplashCardTests
{
private static CancellationToken Token => TestContext.Current.CancellationToken;
/// A message about as long as a real one gets: a path, a reason and a suggestion.
private const string LongFailure =
"The process cannot access the file 'C:\\Users\\somebody\\AppData\\Local\\DodoSSH\\cache.db' "
+ "because it is being used by another process, and this build cannot open a second copy of it.";
///
/// Pins the two constants and the mechanism against the XAML, the way
/// LayoutHarnessTests.TheHarnessMeasuresTheSizeTheWindowDeclares pins the main window's minimum.
/// SizeToContent is the one that matters: a window that lost it would clip the message and this
/// suite's other test would go on passing, because the card would still be measuring the taller size it
/// was never given.
///
[Fact]
public async Task TheSplashIsDeclaredTheWayTheHarnessMeasuresIt()
{
await LayoutHarness.OnTheUiThreadAsync(
() =>
{
var window = new SplashWindow();
try
{
window.Width.ShouldBe(LayoutHarness.SplashWidth);
window.MinHeight.ShouldBe(LayoutHarness.SplashMinimumHeight);
window.SizeToContent.ShouldBe(
SizeToContent.Height,
"a fixed height would clip a long failure message");
}
finally
{
window.Close();
}
},
Token);
}
///
/// The ordinary state has to fit the floor, or the window opens taller than its own minimum for the one
/// line it normally shows — which would make a number
/// that never applies to anything.
///
[Fact]
public async Task TheStartingMessageFitsTheHeightTheSplashOpensAt()
{
await LayoutHarness.OnTheUiThreadAsync(
() => MessageHeight(card => { }).ShouldBeLessThanOrEqualTo(
LayoutHarness.SplashMinimumHeight,
"the splash opens at its minimum and the one line it starts with has to fit"),
Token);
}
///
/// ◆ The test with something behind it. A message that wraps to three lines has to make the card ask for
/// more room; if it did not, the text would be sitting under the bottom edge of the window and the only
/// thing the splash exists to carry at that point would be the part nobody could read.
///
[Fact]
public async Task ALongFailureMessageAsksForMoreRoomRatherThanBeingClipped()
{
await LayoutHarness.OnTheUiThreadAsync(
() =>
{
var starting = MessageHeight(card => { });
var failed = MessageHeight(card => card.CouldNotStart(LongFailure));
failed.ShouldBeGreaterThan(
starting,
"the message wraps, so the card has to want the height the window then gives it");
},
Token);
}
///
/// What one card wants to be, at the splash's width, after the caller has had its say.
///
///
/// Hosted in a window before being measured, because the heading and the hint take their size and their
/// wrapping from App.axaml — a card measured detached would be measuring unstyled text. Measured
/// again afterwards against an unbounded height, which is what the real window's SizeToContent
/// gives it and what the host window's own fixed height would otherwise clamp away.
///
private static double MessageHeight(Action say)
{
var card = new SplashCard();
var window = LayoutHarness.HostAtMinimumSize(
card, LayoutHarness.SplashWidth, LayoutHarness.SplashMinimumHeight);
try
{
say(card);
card.Measure(new Avalonia.Size(LayoutHarness.SplashWidth, double.PositiveInfinity));
return card.DesiredSize.Height;
}
finally
{
window.Close();
}
}
}