From 6ca0648312799251d6ba168ed570568f565dbabc Mon Sep 17 00:00:00 2001 From: AdamMagued Date: Fri, 2 Oct 2026 01:26:52 +0300 Subject: [PATCH] chore(tutorials): go mod tidy and add regression tests --- tutorials/basics/main_test.go | 69 +++++++++++++++++++++++++++++++++ tutorials/commands/main_test.go | 66 +++++++++++++++++++++++++++++++ tutorials/go.mod | 23 +++++------ tutorials/go.sum | 38 +++++++++--------- 4 files changed, 163 insertions(+), 33 deletions(-) create mode 100644 tutorials/basics/main_test.go create mode 100644 tutorials/commands/main_test.go diff --git a/tutorials/basics/main_test.go b/tutorials/basics/main_test.go new file mode 100644 index 0000000000..2620e3448a --- /dev/null +++ b/tutorials/basics/main_test.go @@ -0,0 +1,69 @@ +package main + +import ( + "strings" + "testing" + + tea "charm.land/bubbletea/v2" +) + +func TestBasicsModel(t *testing.T) { + m := initialModel() + if len(m.choices) == 0 { + t.Fatal("expected non-empty initial choices") + } + if m.cursor != 0 { + t.Fatalf("expected initial cursor 0, got %d", m.cursor) + } + if m.Init() != nil { + t.Fatal("expected nil Init command") + } + + // Move cursor down + res, _ := m.Update(tea.KeyPressMsg{Text: "down"}) + m = res.(model) + if m.cursor != 1 { + t.Fatalf("expected cursor 1 after down, got %d", m.cursor) + } + + // Toggle selection on item 1 + res, _ = m.Update(tea.KeyPressMsg{Text: "enter"}) + m = res.(model) + if _, ok := m.selected[1]; !ok { + t.Fatal("expected item 1 to be selected") + } + + // Move cursor up + res, _ = m.Update(tea.KeyPressMsg{Text: "up"}) + m = res.(model) + if m.cursor != 0 { + t.Fatalf("expected cursor 0 after up, got %d", m.cursor) + } + + // Toggle selection off on item 1 + res, _ = m.Update(tea.KeyPressMsg{Text: "down"}) + m = res.(model) + res, _ = m.Update(tea.KeyPressMsg{Text: "enter"}) + m = res.(model) + if _, ok := m.selected[1]; ok { + t.Fatal("expected item 1 to be deselected") + } + + // Quit command + _, cmd := m.Update(tea.KeyPressMsg{Text: "q"}) + if cmd == nil { + t.Fatal("expected non-nil quit command") + } + if _, ok := cmd().(tea.QuitMsg); !ok { + t.Fatalf("expected QuitMsg from quit command, got %T", cmd()) + } + + // View output + view := m.View() + if !strings.Contains(view.Content, "What should we buy at the market?") { + t.Fatalf("expected title in view content, got %q", view.Content) + } + if view.WindowTitle != "Grocery List" { + t.Fatalf("expected window title %q, got %q", "Grocery List", view.WindowTitle) + } +} diff --git a/tutorials/commands/main_test.go b/tutorials/commands/main_test.go new file mode 100644 index 0000000000..6f2dedc11d --- /dev/null +++ b/tutorials/commands/main_test.go @@ -0,0 +1,66 @@ +package main + +import ( + "errors" + "strings" + "testing" + + tea "charm.land/bubbletea/v2" +) + +func TestCommandsModel(t *testing.T) { + m := model{} + + // Verify Init returns checkServer command + if m.Init() == nil { + t.Fatal("expected non-nil Init command") + } + + // Update with statusMsg + res, cmd := m.Update(statusMsg(200)) + m = res.(model) + if m.status != 200 { + t.Fatalf("expected status 200, got %d", m.status) + } + if cmd == nil { + t.Fatal("expected quit command on statusMsg") + } + if _, ok := cmd().(tea.QuitMsg); !ok { + t.Fatalf("expected QuitMsg, got %T", cmd()) + } + + // View with success status + view := m.View() + if !strings.Contains(view.Content, "200 OK!") { + t.Fatalf("expected status text in view content, got %q", view.Content) + } + + // Update with errMsg + testErr := errors.New("connection failed") + res, cmd = m.Update(errMsg{err: testErr}) + m = res.(model) + if m.err == nil || m.err.Error() != "connection failed" { + t.Fatalf("expected error message, got %v", m.err) + } + if cmd == nil { + t.Fatal("expected quit command on errMsg") + } + if _, ok := cmd().(tea.QuitMsg); !ok { + t.Fatalf("expected QuitMsg, got %T", cmd()) + } + + // View with error + view = m.View() + if !strings.Contains(view.Content, "We had some trouble: connection failed") { + t.Fatalf("expected error description in view content, got %q", view.Content) + } + + // Key Ctrl+C + _, cmd = m.Update(tea.KeyPressMsg{Code: 'c', Mod: tea.ModCtrl}) + if cmd == nil { + t.Fatal("expected quit command on Ctrl+C") + } + if _, ok := cmd().(tea.QuitMsg); !ok { + t.Fatalf("expected QuitMsg, got %T", cmd()) + } +} diff --git a/tutorials/go.mod b/tutorials/go.mod index 96173ca470..ebbbca2d89 100644 --- a/tutorials/go.mod +++ b/tutorials/go.mod @@ -1,29 +1,26 @@ module tutorial -go 1.24.2 - -toolchain go1.24.10 +go 1.26.0 require charm.land/bubbletea/v2 v2.0.0-00010101000000-000000000000 require ( - github.com/charmbracelet/colorprofile v0.4.1 // indirect - github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect - github.com/charmbracelet/x/ansi v0.11.6 // indirect + github.com/charmbracelet/colorprofile v0.4.3 // indirect + github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7 // indirect + github.com/charmbracelet/x/ansi v0.11.7 // indirect github.com/charmbracelet/x/term v0.2.2 // indirect github.com/charmbracelet/x/termios v0.1.1 // indirect github.com/charmbracelet/x/windows v0.2.2 // indirect - github.com/clipperhouse/displaywidth v0.9.0 // indirect - github.com/clipperhouse/stringish v0.1.1 // indirect - github.com/clipperhouse/uax29/v2 v2.5.0 // indirect - github.com/lucasb-eyer/go-colorful v1.3.0 // indirect - github.com/mattn/go-runewidth v0.0.19 // indirect + github.com/clipperhouse/displaywidth v0.11.0 // indirect + github.com/clipperhouse/uax29/v2 v2.7.0 // indirect + github.com/lucasb-eyer/go-colorful v1.4.0 // indirect + github.com/mattn/go-runewidth v0.0.23 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.40.0 // indirect + golang.org/x/sync v0.21.0 // indirect + golang.org/x/sys v0.46.0 // indirect ) replace charm.land/bubbletea/v2 => ../ diff --git a/tutorials/go.sum b/tutorials/go.sum index 77fb7cbfb5..3910d7e380 100644 --- a/tutorials/go.sum +++ b/tutorials/go.sum @@ -1,11 +1,11 @@ github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= -github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk= -github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk= -github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 h1:eyFRbAmexyt43hVfeyBofiGSEmJ7krjLOYt/9CF5NKA= -github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8/go.mod h1:SQpCTRNBtzJkwku5ye4S3HEuthAlGy2n9VXZnWkEW98= -github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8= -github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ= +github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q= +github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q= +github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7 h1:3FmWoGNWK4STvqg0O0Aeav2T7rodWJAPeF0QpH+8gFw= +github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7/go.mod h1:f/jRa757WUmaOZrbPspXymbg/GnbF+rwe4OLsG7aXYo= +github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI= +github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ= github.com/charmbracelet/x/exp/golden v0.0.0-20241212170349-ad4b7ae0f25f h1:UytXHv0UxnsDFmL/7Z9Q5SBYPwSuRLXHbwx+6LycZ2w= github.com/charmbracelet/x/exp/golden v0.0.0-20241212170349-ad4b7ae0f25f/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= @@ -14,16 +14,14 @@ github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8 github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM= github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k= -github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA= -github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA= -github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= -github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= -github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U= -github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= -github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= -github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= -github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8= +github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= +github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= +github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= +github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4= +github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw= +github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= @@ -32,7 +30,7 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=