feat(tui): add dashboard.Run bridging engine to Bubble Tea
This commit is contained in:
46
internal/tui/dashboard/run.go
Normal file
46
internal/tui/dashboard/run.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
|
||||||
|
"github.com/MDietermann/ArduinoArchSetup/internal/engine"
|
||||||
|
"github.com/MDietermann/ArduinoArchSetup/internal/module"
|
||||||
|
"github.com/MDietermann/ArduinoArchSetup/internal/tui"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run displays the dashboard while engine.Run executes the selected modules.
|
||||||
|
// Returns the engine summary (for writeState) and the top-level error, if any.
|
||||||
|
func Run(ctx context.Context, reg *module.Registry, state tui.WizardState, halt bool) (engine.Summary, error) {
|
||||||
|
selected := state.SelectedIDs()
|
||||||
|
nameByID := make(map[string]string, len(state.Modules))
|
||||||
|
for _, m := range state.Modules {
|
||||||
|
nameByID[m.ID] = m.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
m := NewModel(selected, nameByID)
|
||||||
|
p := tea.NewProgram(m, tea.WithContext(ctx))
|
||||||
|
|
||||||
|
reporter := tui.NewTeaReporter(p)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
summary, err := engine.Run(ctx, reg, engine.RunOptions{
|
||||||
|
Selected: selected,
|
||||||
|
HaltOnError: halt,
|
||||||
|
Reporter: reporter,
|
||||||
|
})
|
||||||
|
p.Send(DoneMsg{Summary: summary, Err: err})
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err := p.Run(); err != nil {
|
||||||
|
return engine.Summary{}, fmt.Errorf("tui run: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum engine.Summary
|
||||||
|
if m.Summary() != nil {
|
||||||
|
sum = *m.Summary()
|
||||||
|
}
|
||||||
|
return sum, m.RunErr()
|
||||||
|
}
|
||||||
43
internal/tui/dashboard/run_test.go
Normal file
43
internal/tui/dashboard/run_test.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/x/exp/teatest"
|
||||||
|
|
||||||
|
"github.com/MDietermann/ArduinoArchSetup/internal/engine"
|
||||||
|
"github.com/MDietermann/ArduinoArchSetup/internal/tui"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestModelEndToEnd drives the Model through teatest, feeding in the same
|
||||||
|
// message sequence that the TeaReporter + engine goroutine would produce.
|
||||||
|
func TestModelEndToEnd(t *testing.T) {
|
||||||
|
m := NewModel([]string{"alpha"}, map[string]string{"alpha": "Alpha"})
|
||||||
|
tm := teatest.NewTestModel(t, m, teatest.WithInitialTermSize(80, 24))
|
||||||
|
|
||||||
|
tm.Send(tui.ProgressMsg{ModuleID: "alpha", Current: 1, Total: 1, Message: "installing"})
|
||||||
|
tm.Send(DoneMsg{Summary: engine.Summary{Results: []engine.ModuleResult{
|
||||||
|
{ID: "alpha", Status: engine.StatusSucceeded},
|
||||||
|
}}})
|
||||||
|
tm.Send(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'q'}})
|
||||||
|
|
||||||
|
tm.WaitFinished(t, teatest.WithFinalTimeout(3*time.Second))
|
||||||
|
|
||||||
|
out := readAll(t, tm.FinalOutput(t))
|
||||||
|
if !bytes.Contains(out, []byte("alpha")) || !bytes.Contains(out, []byte("summary:")) {
|
||||||
|
t.Fatalf("unexpected final output:\n%s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAll(t *testing.T, r io.Reader) []byte {
|
||||||
|
t.Helper()
|
||||||
|
b, err := io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user