revert(desktop): restore Preparing error state in onboarding

Reverts the Preparing component changes from b2857110b so the progress bar
turns red (bg-destructive) and the error text shows below it when boot.error
is set, instead of bailing out with an early return null.

The corresponding e2e guard in waitForBootFailure (e2e/fixtures.ts) that
rejected any progress bar in the DOM is dropped — it now waits for the
failure dialog (Retry/Repair/Use local gateway/Connection settings) or the
"Desktop boot failed" toast. The boot-failure.spec.ts header comment is
updated to match.

Verified: tsc clean, vitest boot-failure-reauth (21/21) + boot-failure-overlay
(3/3) pass, npm run build clean, playwright e2e/boot-failure.spec.ts 2/2 pass.
This commit is contained in:
ethernet 2026-07-20 14:32:13 -04:00
parent 6ddbe8e5a4
commit 33c154e41f
3 changed files with 10 additions and 20 deletions

View file

@ -4,8 +4,7 @@
*
* Injects a fake boot error (HERMES_DESKTOP_BOOT_FAKE_ERROR) so the backend
* resolution fails with a controlled error message. The app should show the
* BootFailureOverlay with retry/repair actions no progress bar should be
* visible.
* BootFailureOverlay with retry/repair actions.
*
* Prerequisite: `npm run build` must have been run so dist/ exists.
*/

View file

@ -651,16 +651,9 @@ export async function waitForBootFailure(page: Page, timeoutMs = 60_000): Promis
// Boot failure is terminal: the backend gave up. The renderer shows
// either BootFailureOverlay (z-1400, with Retry/Repair buttons) or
// falls back to the onboarding picker (z-1300) as a recovery path.
// Either way, no progress bar should be visible and an error must
// have surfaced (toast, boot-failure banner, or the overlay itself).
const hasProgressBar = Boolean(
document.querySelector('[role="progressbar"], .h-2.rounded-full.bg-muted')
)
if (hasProgressBar) {
return false
}
// We wait for the failure dialog itself — the Preparing component may
// still paint its progress bar (recolored red) underneath the overlay,
// which is harmless.
const text = document.body.textContent ?? ''
// BootFailureOverlay buttons.

View file

@ -355,15 +355,9 @@ function ReasonNotice({ reason }: { reason: string }) {
function Preparing({ boot }: { boot: DesktopBootState }) {
const { t } = useI18n()
const progress = Math.max(2, Math.min(100, Math.round(boot.progress)))
const hasError = Boolean(boot.error)
const installing = boot.phase.startsWith('runtime.')
// When boot fails, BootFailureOverlay (z-1400) owns the screen. Bail out
// here so we never paint a progress bar alongside the error overlay —
// E2E screenshots can catch the two mid-transition.
if (boot.error) {
return null
}
return (
<div className="grid gap-3" role="status">
<p className="text-sm text-muted-foreground">
@ -371,7 +365,10 @@ function Preparing({ boot }: { boot: DesktopBootState }) {
</p>
<div className="h-2 overflow-hidden rounded-full bg-muted">
<div
className="h-full rounded-full bg-primary transition-[width] duration-300 ease-out"
className={cn(
'h-full rounded-full bg-primary transition-[width] duration-300 ease-out',
hasError && 'bg-destructive'
)}
style={{ width: `${progress}%` }}
/>
</div>
@ -379,6 +376,7 @@ function Preparing({ boot }: { boot: DesktopBootState }) {
<span className="truncate">{boot.message}</span>
<span>{progress}%</span>
</div>
{hasError ? <p className="text-xs text-destructive">{boot.error}</p> : null}
</div>
)
}