diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj
index 8fd390881..b8c6bc784 100644
--- a/Terminal.Gui/Terminal.Gui.csproj
+++ b/Terminal.Gui/Terminal.Gui.csproj
@@ -87,6 +87,7 @@
+
diff --git a/Terminal.sln b/Terminal.sln
index 0399a634d..c233b37ad 100644
--- a/Terminal.sln
+++ b/Terminal.sln
@@ -62,6 +62,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StressTests", "Tests\Stress
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests.Parallelizable", "Tests\UnitTestsParallelizable\UnitTests.Parallelizable.csproj", "{DE780834-190A-8277-51FD-750CC666E82D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerminalGuiFluentAssertions", "TerminalGuiFluentAssertions\TerminalGuiFluentAssertions.csproj", "{7C610F03-9E38-409F-9B21-A02D5569E16A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -116,6 +118,10 @@ Global
{DE780834-190A-8277-51FD-750CC666E82D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE780834-190A-8277-51FD-750CC666E82D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE780834-190A-8277-51FD-750CC666E82D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7C610F03-9E38-409F-9B21-A02D5569E16A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7C610F03-9E38-409F-9B21-A02D5569E16A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7C610F03-9E38-409F-9B21-A02D5569E16A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7C610F03-9E38-409F-9B21-A02D5569E16A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/TerminalGuiFluentAssertions/Class1.cs b/TerminalGuiFluentAssertions/Class1.cs
new file mode 100644
index 000000000..596be8d47
--- /dev/null
+++ b/TerminalGuiFluentAssertions/Class1.cs
@@ -0,0 +1,155 @@
+using System.Collections.Concurrent;
+using System.Drawing;
+using Terminal.Gui;
+
+namespace TerminalGuiFluentAssertions;
+
+class FakeInput : IConsoleInput
+{
+ ///
+ public void Dispose () { }
+
+ ///
+ public void Initialize (ConcurrentQueue inputBuffer) { }
+
+ ///
+ public void Run (CancellationToken token)
+ {
+ // Simulate an infinite loop that checks for cancellation
+ token.WaitHandle.WaitOne (); // Blocks until the token is cancelled
+ }
+}
+
+class FakeNetInput : FakeInput, INetInput
+{
+
+}
+
+class FakeWindowsInput : FakeInput, IWindowsInput
+{
+
+}
+
+class FakeOutput : IConsoleOutput
+{
+ public Size Size { get; set; }
+
+ ///
+ public void Dispose ()
+ {
+
+ }
+
+ ///
+ public void Write (ReadOnlySpan text)
+ {
+
+ }
+
+ ///
+ public void Write (IOutputBuffer buffer)
+ {
+
+ }
+
+ ///
+ public Size GetWindowSize ()
+ {
+ return Size;
+ }
+
+ ///
+ public void SetCursorVisibility (CursorVisibility visibility)
+ {
+
+ }
+
+ ///
+ public void SetCursorPosition (int col, int row)
+ {
+
+ }
+
+}
+///
+/// Entry point to fluent assertions.
+///
+public static class With
+{
+ ///
+ /// Entrypoint to fluent assertions
+ ///
+ ///
+ ///
+ ///
+ public static GuiTestContext A (int width, int height) where T : Toplevel, new ()
+ {
+ return new GuiTestContext (width,height);
+ }
+}
+public class GuiTestContext where T : Toplevel, new()
+{
+ private readonly CancellationTokenSource _cts;
+ private readonly Task _runTask;
+
+ internal GuiTestContext (int width, int height)
+ {
+ IApplication origApp = ApplicationImpl.Instance;
+
+ var netInput = new FakeNetInput ();
+ var winInput = new FakeWindowsInput ();
+ var output = new FakeOutput ();
+
+ output.Size = new (width, height);
+
+ var v2 = new ApplicationV2(
+ () => netInput,
+ ()=>output,
+ () => winInput,
+ () => output);
+
+ // Create a cancellation token
+ _cts = new ();
+
+ // Start the application in a background thread
+ _runTask = Task.Run (() =>
+ {
+ try
+ {
+ ApplicationImpl.ChangeInstance (v2);
+
+ v2.Init ();
+
+ Application.Run (); // This will block, but it's on a background thread now
+
+ Application.Shutdown ();
+ }
+ catch (OperationCanceledException)
+ {
+
+ }
+ finally
+ {
+ ApplicationImpl.ChangeInstance (origApp);
+ }
+ }, _cts.Token);
+
+ Application.Shutdown ();
+ }
+
+ ///
+ /// Stops the application and waits for the background thread to exit.
+ ///
+ public void Stop ()
+ {
+ _cts.Cancel ();
+ Application.Invoke (()=>Application.RequestStop());
+ _runTask.Wait (); // Ensure the background thread exits
+ }
+
+ // Cleanup to avoid state bleed between tests
+ public void Dispose ()
+ {
+ }
+}
+
diff --git a/TerminalGuiFluentAssertions/TerminalGuiFluentAssertions.csproj b/TerminalGuiFluentAssertions/TerminalGuiFluentAssertions.csproj
new file mode 100644
index 000000000..629e31cbe
--- /dev/null
+++ b/TerminalGuiFluentAssertions/TerminalGuiFluentAssertions.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/Tests/UnitTests/ConsoleDrivers/V2/ApplicationV2Tests.cs b/Tests/UnitTests/ConsoleDrivers/V2/ApplicationV2Tests.cs
index 1a87b6b59..c1006cf51 100644
--- a/Tests/UnitTests/ConsoleDrivers/V2/ApplicationV2Tests.cs
+++ b/Tests/UnitTests/ConsoleDrivers/V2/ApplicationV2Tests.cs
@@ -6,7 +6,7 @@ using Moq;
namespace UnitTests.ConsoleDrivers.V2;
public class ApplicationV2Tests
{
-
+
private ApplicationV2 NewApplicationV2 ()
{
var netInput = new Mock ();
diff --git a/Tests/UnitTests/FluentTests/BasicFluentAssertionTests.cs b/Tests/UnitTests/FluentTests/BasicFluentAssertionTests.cs
new file mode 100644
index 000000000..1a3473c6c
--- /dev/null
+++ b/Tests/UnitTests/FluentTests/BasicFluentAssertionTests.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TerminalGuiFluentAssertions;
+
+namespace UnitTests.FluentTests;
+public class BasicFluentAssertionTests
+{
+ [Fact]
+ public void GuiTestContext_StartsAndStopsWithoutError ()
+ {
+ var context = With.A (40, 10);
+
+ // No actual assertions are needed — if no exceptions are thrown, it's working
+ context.Stop ();
+ }
+
+ [Fact]
+ public void Test ()
+ {
+ var myView = new TextField () { Width = 10 };
+
+
+
+ /*
+ using var ctx = With.A (20, 10)
+ .Add (myView, 3, 2)
+ .Focus (myView)
+ .Type ("Hello");
+
+ Assert.Equal ("Hello", myView.Text);*/
+ }
+}
diff --git a/Tests/UnitTests/UnitTests.csproj b/Tests/UnitTests/UnitTests.csproj
index fa8470749..27dd519fa 100644
--- a/Tests/UnitTests/UnitTests.csproj
+++ b/Tests/UnitTests/UnitTests.csproj
@@ -45,6 +45,7 @@
+