Desktop: give the host-key, delete and sign-out questions the keyboard on their harmless answer, and let Escape say no

This commit is contained in:
jaap-jan committed 2026-10-02 13:45:40 +02:00
1 parent 372a7cbdc7
commit 6c09bdecb6
11 files changed
+498 -92

No files matched your search

@@ -1,16 +1,10 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Threading;
using DodoSSH.Client.App.Views;
using DodoSSH.Client.Session;
using DodoSSH.Client.Session.Tests;
using DodoSSH.Client.Shell.ViewModels;
using DodoSSH.Client.Ssh;
using DodoSSH.Client.Storage;
using DodoSSH.Client.Terminal;
using DodoSSH.Client.Transfer;
using DodoSSH.Crypto;
using NSubstitute;
namespace DodoSSH.Client.App.Layout.Tests;
@@ -24,18 +18,11 @@ namespace DodoSSH.Client.App.Layout.Tests;
/// and Enter in the server address did nothing at all.
/// </para>
/// <para>
/// The whole window, which <see cref="LayoutHarnessTests.WhyTheWindowItselfIsNeverShown"/> says cannot be shown
/// here: the terminal's WebView refuses this thread's apartment when it is attached. It is taken out of the
/// tree before the window is shown, which is everything that test is about and nothing these are.
/// The whole window, shown the way <see cref="WholeWindow"/> describes.
/// </para>
/// </remarks>
public sealed class ClosedVaultFocusTests
{
private const string Passphrase = "a sufficiently long passphrase";
private static readonly Argon2Profile CheapProfile =
Argon2Profile.FromStoredParameters(memoryKibibytes: 8 * 1024, passes: 1, parallelism: 1);
private static CancellationToken Token => TestContext.Current.CancellationToken;
[Fact]
@@ -44,14 +31,14 @@ public sealed class ClosedVaultFocusTests
using var caches = ClientCacheFactory.ForMemory($"focus-{Guid.CreateVersion7():N}");
await caches.MigrateAsync(Token);
await using var workspace = Workspace();
await using var shell = Shell(caches, workspace);
await using var workspace = WholeWindow.Workspace();
await using var shell = WholeWindow.Shell(caches, workspace);
await shell.StartAsync(Token);
shell.State.ShouldBe(ShellState.NeedsServer);
await LayoutHarness.OnTheUiThreadAsync(
() => WithWindow(shell, window =>
() => WholeWindow.Show(shell, window =>
{
var box = window.FindControl<TextBox>("ServerUrlBox").ShouldNotBeNull();
@@ -69,59 +56,19 @@ public sealed class ClosedVaultFocusTests
using var caches = ClientCacheFactory.ForMemory($"focus-{Guid.CreateVersion7():N}");
await caches.MigrateAsync(Token);
await new AccountProvisioner(new FakeAccountServer(), new StubKeyBinding(), caches, TimeProvider.System, CheapProfile)
.EnrollAsync("https://dodossh.example", Passphrase, "laptop", "Personal", Token);
await new AccountProvisioner(new FakeAccountServer(), new StubKeyBinding(), caches, TimeProvider.System, WholeWindow.CheapProfile)
.EnrollAsync("https://dodossh.example", WholeWindow.Passphrase, "laptop", "Personal", Token);
await using var workspace = Workspace();
await using var shell = Shell(caches, workspace);
await using var workspace = WholeWindow.Workspace();
await using var shell = WholeWindow.Shell(caches, workspace);
await shell.StartAsync(Token);
shell.State.ShouldBe(ShellState.Locked);
await LayoutHarness.OnTheUiThreadAsync(
() => WithWindow(shell, window =>
() => WholeWindow.Show(shell, window =>
window.FocusManager!.GetFocusedElement()
.ShouldBeSameAs(window.FindControl<UnlockCard>("UnlockPane").ShouldNotBeNull().PassphraseBox)),
Token);
}
private static TerminalWorkspace Workspace() =>
new(
new InMemoryTerminalAssetProvider(new Dictionary<string, TerminalAsset>(StringComparer.Ordinal)),
Substitute.For<ISshConnectionFactory>(),
TimeProvider.System);
private static MainWindowViewModel Shell(ClientCacheFactory caches, TerminalWorkspace workspace) =>
new(
new ClientPaths(Path.Combine(Path.GetTempPath(), $"dodossh-focus-{Guid.CreateVersion7():N}")),
caches,
workspace,
new VaultKnownHostStore(),
new UnavailableDeviceKeyStore(),
static (_, _) => throw new InvalidOperationException("A layout test has no network."),
TimeProvider.System,
Substitute.For<ISftpSessionFactory>(),
CheapProfile);
private static void WithWindow(MainWindowViewModel shell, Action<MainWindow> assert)
{
var window = new MainWindow { DataContext = shell };
if (window.FindControl<NativeWebView>("Terminal") is { Parent: Panel panel } web)
{
panel.Children.Remove(web);
}
try
{
LayoutHarness.Settle(window, LayoutHarness.MinimumWidth, LayoutHarness.MinimumHeight);
Dispatcher.UIThread.RunJobs();
assert(window);
}
finally
{
window.Close();
}
}
}