From e778c3c443de0b299c7b4f74999969c4af8010c1 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Sat, 5 Apr 2025 21:05:34 +0200 Subject: [PATCH] fix(ui): mediaserver error modal improvements --- internal/app/integration_test.go | 54 ++++++++++++++++++++++++++++++++ internal/terminal/terminal.go | 33 +++++++++---------- 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/internal/app/integration_test.go b/internal/app/integration_test.go index bbc51a5..85145e5 100644 --- a/internal/app/integration_test.go +++ b/internal/app/integration_test.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "log/slog" + "net" "os" "runtime" "strings" @@ -476,6 +477,59 @@ func TestIntegrationStartupCheck(t *testing.T) { <-done } +func TestIntegrationMediaServerError(t *testing.T) { + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Minute) + defer cancel() + + lis, err := net.Listen("tcp", ":1935") + require.NoError(t, err) + t.Cleanup(func() { lis.Close() }) + + logger := testhelpers.NewTestLogger(t).With("component", "integration") + dockerClient, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv, dockerclient.WithAPIVersionNegotiation()) + require.NoError(t, err) + + configService := setupConfigService(t, config.Config{Sources: config.Sources{RTMP: config.RTMPSource{Enabled: true}}}) + screen, screenCaptureC, getContents := setupSimulationScreen(t) + + done := make(chan struct{}) + go func() { + err := app.Run(ctx, app.RunParams{ + ConfigService: configService, + DockerClient: dockerClient, + Screen: &terminal.Screen{ + Screen: screen, + Width: 200, + Height: 25, + CaptureC: screenCaptureC, + }, + ClipboardAvailable: false, + BuildInfo: domain.BuildInfo{Version: "0.0.1", GoVersion: "go1.16.3"}, + Logger: logger, + }) + require.NoError(t, err) + + done <- struct{}{} + }() + + require.EventuallyWithT( + t, + func(t *assert.CollectT) { + assert.True(t, contentsIncludes(getContents(), "Mediaserver error: Server process exited unexpectedly."), "expected to see title") + assert.True(t, contentsIncludes(getContents(), "address already in use"), "expected to see message") + }, + time.Minute, + time.Second, + "expected to see media server error modal", + ) + printScreen(getContents, "Ater displaying the media server error modal") + + // Quit the app, this should cause the done channel to receive. + sendKey(screen, tcell.KeyEnter, ' ') + + <-done +} + func setupSimulationScreen(t *testing.T) (tcell.SimulationScreen, chan<- terminal.ScreenCapture, func() []string) { // Fetching the screen contents is tricky at this level of the test pyramid, // because we need to: diff --git a/internal/terminal/terminal.go b/internal/terminal/terminal.go index 90d9cab..055d4fc 100644 --- a/internal/terminal/terminal.go +++ b/internal/terminal/terminal.go @@ -480,16 +480,17 @@ func (ui *UI) updateProgressModal(container domain.Container) { // on top of other modals. const ( pageNameMain = "main" - pageNameNoDestinations = "no-destinations" pageNameAddDestination = "add-destination" - pageNameModalDestinationError = "modal-destination-error" - pageNameModalAbout = "modal-about" - pageNameModalQuit = "modal-quit" - pageNameModalStartupCheck = "modal-startup-check" - pageNameModalClipboard = "modal-clipboard" - pageNameModalPullProgress = "modal-pull-progress" - pageNameModalRemoveDestination = "modal-remove-destination" pageNameConfigUpdateFailed = "modal-config-update-failed" + pageNameNoDestinations = "no-destinations" + pageNameModalAbout = "modal-about" + pageNameModalClipboard = "modal-clipboard" + pageNameModalDestinationError = "modal-destination-error" + pageNameModalPullProgress = "modal-pull-progress" + pageNameModalQuit = "modal-quit" + pageNameModalRemoveDestination = "modal-remove-destination" + pageNameModalSourceError = "modal-source-error" + pageNameModalStartupCheck = "modal-startup-check" ) func (ui *UI) resetFocus() { @@ -540,26 +541,23 @@ func (ui *UI) hideModal(pageName string) { } func (ui *UI) handleMediaServerClosed(exitReason string) { - done := make(chan struct{}) - ui.app.QueueUpdateDraw(func() { + if ui.pages.HasPage(pageNameModalSourceError) { + return + } + modal := tview.NewModal() modal.SetText("Mediaserver error: " + exitReason). AddButtons([]string{"Quit"}). SetBackgroundColor(tcell.ColorBlack). SetTextColor(tcell.ColorWhite). SetDoneFunc(func(int, string) { - // TODO: improve app cleanup - done <- struct{}{} - - ui.app.Stop() + ui.commandCh <- CommandQuit{} }) modal.SetBorderStyle(tcell.StyleDefault.Background(tcell.ColorBlack).Foreground(tcell.ColorWhite)) - ui.pages.AddPage("modal", modal, true, true) + ui.pages.AddPage(pageNameModalSourceError, modal, true, true) }) - - <-done } const dash = "—" @@ -904,7 +902,6 @@ func (ui *UI) confirmQuit() { func(buttonIndex int, _ string) { if buttonIndex == 0 { ui.commandCh <- CommandQuit{} - return } }, )