package tui import ( "os" "path/filepath" "strings" "testing" tea "charm.land/bubbletea/v2" "gitgud.io/mike/mpv-manager/pkg/config" ) // newInstallPathModel sandboxes the user config, reloads the global config // from the sandbox, and returns a model in the install path menu state. func newInstallPathModel(t *testing.T) *Model { t.Helper() sandboxConfigHome(t) if _, err := config.Reload(); err != nil { t.Fatalf("failed to reload config: %v", err) } m := NewModel() m.setupInstallPathList() m.state = StateInstallPath return m } func TestInstallPathEditInvalidKeepsState(t *testing.T) { m := newInstallPathModel(t) defaultPath := config.GetInstallPath() // Enter the edit screen m.installPathList.Select(0) m = pressKey(t, m, tea.KeyPressMsg{Code: tea.KeyEnter}) if m.state != StateInstallPathEdit { t.Fatalf("expected StateInstallPathEdit, got %v", m.state) } // A relative path fails validation inline; stored path must not change m.installPathTextInput.SetValue("relative/path") m = pressKey(t, m, tea.KeyPressMsg{Code: tea.KeyEnter}) if m.state != StateInstallPathEdit { t.Fatalf("expected to stay in StateInstallPathEdit, got %v", m.state) } if !strings.HasPrefix(m.installPathStatusMsg, "✗") { t.Errorf("expected error status, got %q", m.installPathStatusMsg) } if got := config.GetInstallPath(); got != defaultPath { t.Errorf("expected install path unchanged %q, got %q", defaultPath, got) } // Esc cancels back to the menu m = pressKey(t, m, tea.KeyPressMsg{Code: tea.KeyEsc}) if m.state != StateInstallPath { t.Fatalf("expected StateInstallPath after Esc, got %v", m.state) } } func TestInstallPathSetAndReset(t *testing.T) { m := newInstallPathModel(t) customPath := filepath.Join(t.TempDir(), "custom-mpv") // Set a valid custom path m.installPathList.Select(0) m = pressKey(t, m, tea.KeyPressMsg{Code: tea.KeyEnter}) m.installPathTextInput.SetValue(customPath) m = pressKey(t, m, tea.KeyPressMsg{Code: tea.KeyEnter}) if m.state != StateInstallPath { t.Fatalf("expected StateInstallPath after save, got %v", m.state) } if !strings.HasPrefix(m.installPathStatusMsg, "✓") { t.Errorf("expected success status, got %q", m.installPathStatusMsg) } if got := config.GetInstallPath(); got != customPath { t.Errorf("expected install path %q, got %q", customPath, got) } // Reset restores the platform default m.installPathList.Select(1) m = pressKey(t, m, tea.KeyPressMsg{Code: tea.KeyEnter}) if got := config.GetInstallPath(); got == customPath { t.Errorf("expected custom path cleared, still got %q", got) } if !strings.HasPrefix(m.installPathStatusMsg, "✓") { t.Errorf("expected success status, got %q", m.installPathStatusMsg) } } func TestInstallPathEscReturnsToConfigOptions(t *testing.T) { m := newInstallPathModel(t) m = pressKey(t, m, tea.KeyPressMsg{Code: tea.KeyEsc}) if m.state != StateConfigOptions { t.Fatalf("expected StateConfigOptions after Esc, got %v", m.state) } } func TestConfigOptionsContainsNewEntries(t *testing.T) { sandboxConfigHome(t) if _, err := config.Reload(); err != nil { t.Fatalf("failed to reload config: %v", err) } m := NewModel() m.setupConfigOptionsList() ids := map[string]bool{} for _, item := range m.configOptionsList.Items() { ids[item.(menuItem).id] = true } if !ids["install-path"] { t.Error("expected install-path entry in config options") } // ModernZ options require an installed MPV app (none in the sandbox) if ids["modernz-options"] { t.Error("expected modernz-options hidden without an installed MPV app") } // An MPV install alone must not claim that a configurable UI is present. if err := config.AddInstalledApp(config.InstalledApp{ AppName: "mpv", AppType: "exe-binary", InstallMethod: "mpv-binary", }); err != nil { t.Fatalf("failed to add installed app: %v", err) } m.setupConfigOptionsList() ids = map[string]bool{} for _, item := range m.configOptionsList.Items() { ids[item.(menuItem).id] = true } if ids["modernz-options"] || ids["uosc-options"] { t.Error("expected UI settings hidden until a supported UI is detected") } if err := config.SetInstalledUIType("modernz"); err != nil { t.Fatalf("failed to record ModernZ UI: %v", err) } m.setupConfigOptionsList() ids = map[string]bool{} for _, item := range m.configOptionsList.Items() { ids[item.(menuItem).id] = true } if !ids["modernz-options"] || ids["uosc-options"] { t.Error("expected only ModernZ settings when ModernZ is active") } scriptsDir := filepath.Join(config.GetMPVConfigPath(), "scripts") if err := os.MkdirAll(scriptsDir, 0755); err != nil { t.Fatalf("failed to create scripts dir: %v", err) } if err := os.WriteFile(filepath.Join(scriptsDir, "uosc.lua"), []byte("-- test"), 0644); err != nil { t.Fatalf("failed to create uOSC evidence: %v", err) } m.setupConfigOptionsList() ids = map[string]bool{} for _, item := range m.configOptionsList.Items() { ids[item.(menuItem).id] = true } if !ids["uosc-options"] || ids["modernz-options"] { t.Error("expected only uOSC settings when uOSC is active") } }