fix(ui): report failed paste/drop in the interactive terminal

PasteAsync silently swallowed every exception and returned void, so a
package update renaming Iciclecreek's private write seam would break
Ctrl+V/drop with zero user-visible signal. PasteAsync/SendTextAsync now
return bool (mirroring SendLineAsync), and the view checks the result,
routing a failure through InteractiveTerminalViewModel.ErrorReported ->
ConPtyPaneViewModel.ErrorReported -> the existing footer error strip.
This commit is contained in:
mika kuns
2026-09-10 13:35:16 +02:00
parent acff815706
commit 0cec0a77ee
9 changed files with 112 additions and 9 deletions
@@ -35,4 +35,14 @@ public class PtyTerminalSessionTests
Assert.Equal(0, gate.CurrentCount); // acquired
}
// Fix: PasteAsync used to be void and swallow every failure. Not-started is the cheap case to
// cover without a real TerminalControl/ConPTY spawn.
[Fact]
public async Task PasteAsync_ReturnsFalse_WhenNotStarted()
{
using var session = new PtyTerminalSession();
Assert.False(await session.PasteAsync());
}
}
@@ -219,6 +219,35 @@ public class ConPtyPaneViewModelTests
Assert.True(raised);
}
// ── Terminal.ErrorReported forwarding (fix: a failed paste/drop had no path to the footer
// error strip) ────────────────────────────────────────────────────────────────────────────
[Fact]
public void TerminalErrorReported_Forwards_ToPaneErrorReported()
{
using var pane = NewTaskPane(NeverCompletes);
string? error = null;
pane.ErrorReported += msg => error = msg;
pane.Terminal.ReportSendFailed();
Assert.NotNull(error);
}
[Fact]
public void TerminalErrorReported_StillWiredAfterRetry()
{
using var pane = NewTaskPane(() => Failing());
pane.Start();
pane.RetryCommand.Execute(null);
string? error = null;
pane.ErrorReported += msg => error = msg;
pane.Terminal.ReportSendFailed();
Assert.NotNull(error);
}
private static void SetRunning(ConPtyPaneViewModel pane) => pane.Terminal.IsRunning = true;
// Drives the terminal past its startup grace window, then kills it: IsRunning stamps the launch
@@ -124,4 +124,35 @@ public class InteractiveTerminalViewModelTests
terminal.IsRunning = true; // records LaunchedAt via the first clock call
return terminal;
}
// ── Paste/drop failure reporting (fix: PasteAsync used to swallow every exception and return
// void, so a failed Ctrl+V or file drop gave the user no signal at all) ────────────────────
[Fact]
public async Task PasteAsync_ReturnsFalse_WhenSessionNotRunning()
{
using var terminal = new InteractiveTerminalViewModel();
Assert.False(await terminal.PasteAsync());
}
[Fact]
public async Task SendTextAsync_ReturnsFalse_WhenSessionNotRunning()
{
using var terminal = new InteractiveTerminalViewModel();
Assert.False(await terminal.SendTextAsync("dropped/path"));
}
[Fact]
public void ReportSendFailed_RaisesErrorReported()
{
using var terminal = new InteractiveTerminalViewModel();
string? error = null;
terminal.ErrorReported += msg => error = msg;
terminal.ReportSendFailed();
Assert.NotNull(error);
}
}