mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
WIP: Add fluent assertions and prototype the kind of things that should be possible
This commit is contained in:
@@ -1,52 +1,45 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Enable Nuget Source Link for github -->
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="[8,9)" />
|
||||
|
||||
<PackageVersion Include="ColorHelper" Version="[1.8.1,2)" />
|
||||
<PackageVersion Include="JetBrains.Annotations" Version="[2024.3.0,)" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis" Version="[4.13,5)" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="[4.13,5)" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="[4.13,5)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="[9.0.2,10)" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="[22.0.11,23)" />
|
||||
<PackageVersion Include="System.Text.Json" Version="[8.0.5,9)" />
|
||||
<PackageVersion Include="Wcwidth" Version="[2,3)" />
|
||||
|
||||
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="[1.21.2,2)" />
|
||||
<PackageVersion Include="Serilog" Version="4.2.0" />
|
||||
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="[3.1.7,4)" />
|
||||
<PackageVersion Include="CsvHelper" Version="[33.0.1,34)" />
|
||||
<PackageVersion Include="Microsoft.DotNet.PlatformAbstractions" Version="[3.1.6,4)" />
|
||||
<PackageVersion Include="System.CommandLine" Version="[2.0.0-beta4.22272.1,3)" />
|
||||
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="[8.4.0,9)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="[9.0.2,10)" />
|
||||
<PackageVersion Include="ReactiveUI" Version="[20.1.63,21)" />
|
||||
<PackageVersion Include="ReactiveMarbles.ObservableEvents.SourceGenerator" Version="[1.3.1,2)" />
|
||||
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="[2.1.8,3)"/>
|
||||
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="[17.13,18)" />
|
||||
<PackageVersion Include="Moq" Version="[4.20.72,5)" />
|
||||
<PackageVersion Include="ReportGenerator" Version="[5.4.4,6)" />
|
||||
<PackageVersion Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="[22.0.11,23)" />
|
||||
<PackageVersion Include="xunit" Version="[2.9.3,3)" />
|
||||
<PackageVersion Include="Xunit.Combinatorial" Version="[1.6.24,2)" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="[2.8.2,3)"/>
|
||||
<PackageVersion Include="coverlet.collector" Version="[6.0.4,7)" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<PackageVersion Include="Terminal.Gui" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Enable Nuget Source Link for github -->
|
||||
<PackageVersion Include="FluentAssertions" Version="8.2.0" />
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="[8,9)" />
|
||||
<PackageVersion Include="ColorHelper" Version="[1.8.1,2)" />
|
||||
<PackageVersion Include="JetBrains.Annotations" Version="[2024.3.0,)" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis" Version="[4.13,5)" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="[4.13,5)" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="[4.13,5)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="[9.0.2,10)" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="[22.0.11,23)" />
|
||||
<PackageVersion Include="System.Text.Json" Version="[8.0.5,9)" />
|
||||
<PackageVersion Include="Wcwidth" Version="[2,3)" />
|
||||
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="[1.21.2,2)" />
|
||||
<PackageVersion Include="Serilog" Version="4.2.0" />
|
||||
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="[3.1.7,4)" />
|
||||
<PackageVersion Include="CsvHelper" Version="[33.0.1,34)" />
|
||||
<PackageVersion Include="Microsoft.DotNet.PlatformAbstractions" Version="[3.1.6,4)" />
|
||||
<PackageVersion Include="System.CommandLine" Version="[2.0.0-beta4.22272.1,3)" />
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="[8.4.0,9)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="[9.0.2,10)" />
|
||||
<PackageVersion Include="ReactiveUI" Version="[20.1.63,21)" />
|
||||
<PackageVersion Include="ReactiveMarbles.ObservableEvents.SourceGenerator" Version="[1.3.1,2)" />
|
||||
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="[2.1.8,3)" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="[17.13,18)" />
|
||||
<PackageVersion Include="Moq" Version="[4.20.72,5)" />
|
||||
<PackageVersion Include="ReportGenerator" Version="[5.4.4,6)" />
|
||||
<PackageVersion Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="[22.0.11,23)" />
|
||||
<PackageVersion Include="xunit" Version="[2.9.3,3)" />
|
||||
<PackageVersion Include="Xunit.Combinatorial" Version="[1.6.24,2)" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="[2.8.2,3)" />
|
||||
<PackageVersion Include="coverlet.collector" Version="[6.0.4,7)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<PackageVersion Include="Terminal.Gui" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,10 +1,12 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Drawing;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Numeric;
|
||||
using Terminal.Gui;
|
||||
|
||||
namespace TerminalGuiFluentAssertions;
|
||||
|
||||
class FakeInput<T> : IConsoleInput<T>
|
||||
class FakeInput<T>(CancellationToken hardStopToken) : IConsoleInput<T>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void Dispose () { }
|
||||
@@ -15,17 +17,17 @@ class FakeInput<T> : IConsoleInput<T>
|
||||
/// <inheritdoc />
|
||||
public void Run (CancellationToken token)
|
||||
{
|
||||
// Simulate an infinite loop that checks for cancellation
|
||||
token.WaitHandle.WaitOne (); // Blocks until the token is cancelled
|
||||
// Blocks until either the token or the hardStopToken is cancelled.
|
||||
WaitHandle.WaitAny (new [] { token.WaitHandle, hardStopToken.WaitHandle });
|
||||
}
|
||||
}
|
||||
|
||||
class FakeNetInput : FakeInput<ConsoleKeyInfo>, INetInput
|
||||
class FakeNetInput (CancellationToken hardStopToken) : FakeInput<ConsoleKeyInfo> (hardStopToken), INetInput
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class FakeWindowsInput : FakeInput<WindowsConsole.InputRecord>, IWindowsInput
|
||||
class FakeWindowsInput (CancellationToken hardStopToken) : FakeInput<WindowsConsole.InputRecord> (hardStopToken), IWindowsInput
|
||||
{
|
||||
|
||||
}
|
||||
@@ -87,29 +89,29 @@ public static class With
|
||||
return new GuiTestContext<T> (width,height);
|
||||
}
|
||||
}
|
||||
public class GuiTestContext<T> where T : Toplevel, new()
|
||||
public class GuiTestContext<T> : IDisposable where T : Toplevel, new()
|
||||
{
|
||||
private readonly CancellationTokenSource _cts;
|
||||
private readonly CancellationTokenSource _cts = new ();
|
||||
private readonly CancellationTokenSource _hardStop = new ();
|
||||
private readonly Task _runTask;
|
||||
private Exception _ex;
|
||||
private readonly FakeOutput _output = new ();
|
||||
|
||||
internal GuiTestContext (int width, int height)
|
||||
{
|
||||
IApplication origApp = ApplicationImpl.Instance;
|
||||
|
||||
var netInput = new FakeNetInput ();
|
||||
var winInput = new FakeWindowsInput ();
|
||||
var output = new FakeOutput ();
|
||||
var netInput = new FakeNetInput (_cts.Token);
|
||||
var winInput = new FakeWindowsInput (_cts.Token);
|
||||
|
||||
output.Size = new (width, height);
|
||||
_output.Size = new (width, height);
|
||||
|
||||
var v2 = new ApplicationV2(
|
||||
() => netInput,
|
||||
()=>output,
|
||||
()=>_output,
|
||||
() => winInput,
|
||||
() => output);
|
||||
() => _output);
|
||||
|
||||
// Create a cancellation token
|
||||
_cts = new ();
|
||||
|
||||
// Start the application in a background thread
|
||||
_runTask = Task.Run (() =>
|
||||
@@ -125,31 +127,90 @@ public class GuiTestContext<T> where T : Toplevel, new()
|
||||
Application.Shutdown ();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{ }
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
_ex = ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ApplicationImpl.ChangeInstance (origApp);
|
||||
}
|
||||
}, _cts.Token);
|
||||
|
||||
Application.Shutdown ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the application and waits for the background thread to exit.
|
||||
/// </summary>
|
||||
public void Stop ()
|
||||
public GuiTestContext<T> Stop ()
|
||||
{
|
||||
if (_runTask.IsCompleted)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
Application.Invoke (()=> Application.RequestStop ());
|
||||
|
||||
// Wait for the application to stop, but give it a 1-second timeout
|
||||
if (!_runTask.Wait (TimeSpan.FromMilliseconds (1000)))
|
||||
{
|
||||
_cts.Cancel ();
|
||||
// Timeout occurred, force the task to stop
|
||||
_hardStop.Cancel ();
|
||||
throw new TimeoutException ("Application failed to stop within the allotted time.");
|
||||
}
|
||||
_cts.Cancel ();
|
||||
Application.Invoke (()=>Application.RequestStop());
|
||||
_runTask.Wait (); // Ensure the background thread exits
|
||||
|
||||
if (_ex != null)
|
||||
{
|
||||
throw _ex; // Propagate any exception that happened in the background task
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// Cleanup to avoid state bleed between tests
|
||||
public void Dispose ()
|
||||
{
|
||||
Stop ();
|
||||
_hardStop.Cancel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given <paramref name="v"/> to the current top level view
|
||||
/// and performs layout.
|
||||
/// </summary>
|
||||
/// <param name="v"></param>
|
||||
/// <returns></returns>
|
||||
public GuiTestContext<T> Add (View v)
|
||||
{
|
||||
Application.Invoke (
|
||||
() =>
|
||||
{
|
||||
var top = Application.Top ?? throw new Exception("Top was null so could not add view");
|
||||
top.Add (v);
|
||||
top.Layout ();
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiTestContext<T> ResizeConsole (int width, int height)
|
||||
{
|
||||
_output.Size = new Size (width,height);
|
||||
|
||||
return WaitIteration ();
|
||||
}
|
||||
public GuiTestContext<T> WaitIteration ()
|
||||
{
|
||||
Application.Invoke (() => { });
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiTestContext<T> Assert<T2> (AndConstraint<T2> be)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using TerminalGuiFluentAssertions;
|
||||
|
||||
namespace UnitTests.FluentTests;
|
||||
@@ -11,25 +12,31 @@ public class BasicFluentAssertionTests
|
||||
[Fact]
|
||||
public void GuiTestContext_StartsAndStopsWithoutError ()
|
||||
{
|
||||
var context = With.A<Window> (40, 10);
|
||||
using var context = With.A<Window> (40, 10);
|
||||
|
||||
// No actual assertions are needed — if no exceptions are thrown, it's working
|
||||
context.Stop ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Test ()
|
||||
public void GuiTestContext_ForgotToStop ()
|
||||
{
|
||||
var myView = new TextField () { Width = 10 };
|
||||
using var context = With.A<Window> (40, 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestWindowsResize ()
|
||||
{
|
||||
var lbl = new Label ()
|
||||
{
|
||||
Width = Dim.Fill ()
|
||||
};
|
||||
using var c = With.A<Window> (40, 10)
|
||||
.Add (lbl )
|
||||
.Assert (lbl.Frame.Width.Should().Be(40))
|
||||
.ResizeConsole (20,20)
|
||||
.Assert (lbl.Frame.Width.Should ().Be (20))
|
||||
.Stop ();
|
||||
|
||||
|
||||
/*
|
||||
using var ctx = With.A<Window> (20, 10)
|
||||
.Add (myView, 3, 2)
|
||||
.Focus (myView)
|
||||
.Type ("Hello");
|
||||
|
||||
Assert.Equal ("Hello", myView.Text);*/
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user