feat(launcher): LU4 — first-run setup ends with "Setup complete" and an OK button

Setup used to finish by leaving a full progress bar and a status line on
screen, with the same Validate / Cancel bake / Close / Build and install row
underneath. Nothing said "you are done" and nothing said what to press.

The wizard now swaps its whole form for a plain completion panel: "Setup
complete", one sentence saying the content was built and verified, and a single
OK that closes the dialog and returns to the launcher.

Raised at exactly one point — after _onInstalled publishes the record — so the
launcher behind the dialog is already in its launch-enabled state when OK is
pressed, and the "Client setup required" banner is gone the moment the user
gets back. The cancelled and failed branches deliberately never reach it and
keep their existing status/error reporting.

Tests: FirstRunSetupEndsWithACompletionPanelThatOkReturnsFrom (form hidden,
panel shown, record published before OK, wizard reopens as an ordinary form
afterwards) and AFailedFirstRunSetupNeverShowsTheCompletionPanel.
Launcher 61 passed.

Campaign LU slice LU4.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-19 18:51:08 +02:00
parent a01ff42640
commit 0a2defb618
3 changed files with 138 additions and 0 deletions

View file

@ -442,6 +442,74 @@ public sealed class LauncherWindowViewModelTests
Assert.True(orchestrator.ClearCalled);
}
/// <summary>
/// LU4. A successful first-run setup ends with a plain "Setup complete"
/// panel and one OK button that returns to the launcher — not a finished
/// progress bar the user has to decide what to do with.
/// </summary>
[Fact]
public async Task FirstRunSetupEndsWithACompletionPanelThatOkReturnsFrom()
{
using var orchestrator = new FakeLauncherOrchestrator
{
Session = FakeLauncherOrchestrator.CreateSession(
LauncherActivityState.Exited,
"Exited cleanly."),
};
var installer = new FakeLauncherInstaller();
using var viewModel = CreateInitialized(orchestrator, installer);
viewModel.FirstRunWizardShell.OpenCommand.Execute(null);
Assert.False(viewModel.FirstRunWizardShell.IsCompleted);
Assert.True(viewModel.FirstRunWizardShell.ShowSetupForm);
await viewModel.FirstRunWizardShell.StartCommand.ExecuteAsync();
Assert.True(viewModel.FirstRunWizardShell.IsCompleted);
// The form is gone; the completion panel is what the user sees.
Assert.False(viewModel.FirstRunWizardShell.ShowSetupForm);
Assert.True(viewModel.FirstRunWizardShell.IsOpen);
// The record is already published, so the launcher behind the dialog
// is in its launch-enabled state before OK is pressed.
Assert.NotNull(orchestrator.InstalledRecord);
viewModel.FirstRunWizardShell.AcknowledgeCompletionCommand.Execute(null);
Assert.False(viewModel.FirstRunWizardShell.IsOpen);
Assert.False(viewModel.FirstRunWizardShell.IsCompleted);
Assert.True(viewModel.FirstRunWizardShell.ShowSetupForm);
}
/// <summary>
/// LU4. Cancellation and failure must NOT claim success. They keep their
/// existing status/error reporting and leave the form on screen.
/// </summary>
[Fact]
public async Task AFailedFirstRunSetupNeverShowsTheCompletionPanel()
{
using var orchestrator = new FakeLauncherOrchestrator
{
Session = FakeLauncherOrchestrator.CreateSession(
LauncherActivityState.Exited,
"Exited cleanly."),
};
var installer = new FakeLauncherInstaller
{
InstallHandler = (_, _, _, _) =>
Task.FromException<LauncherInstallResult>(
new LauncherInstallException("The bake tool exited with code 3.")),
};
using var viewModel = CreateInitialized(orchestrator, installer);
viewModel.FirstRunWizardShell.OpenCommand.Execute(null);
await viewModel.FirstRunWizardShell.StartCommand.ExecuteAsync();
Assert.False(viewModel.FirstRunWizardShell.IsCompleted);
Assert.True(viewModel.FirstRunWizardShell.ShowSetupForm);
Assert.True(viewModel.FirstRunWizardShell.HasError);
Assert.Null(orchestrator.InstalledRecord);
}
/// <summary>
/// LU1. Ordinary startup trusts a remembered digest so the window is not
/// held behind a multi-second hash of a ~28 GiB file; "Verify files" is