Skip to content

Commit f382b69

Browse files
committed
test: adds test coverage for charmPromptTemplateSelection
1 parent 6ac23fb commit f382b69

4 files changed

Lines changed: 137 additions & 11 deletions

File tree

cmd/project/create_template.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory,
108108
if categoryShortcut == "agent" {
109109
categoryID = "slack-cli#ai-apps"
110110
} else if clients.Config.WithExperimentOn(experiment.Charm) {
111-
result, err := charmPromptTemplateSelectionFunc(ctx, clients)
111+
result, err := charmPromptTemplateSelection(ctx, clients)
112112
if err != nil {
113113
return create.Template{}, slackerror.ToSlackError(err)
114114
}

cmd/project/create_template_charm.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ type templateSelectionResult struct {
3131
TemplateRepo string // e.g. "slack-samples/bolt-js-starter-template"
3232
}
3333

34-
// charmPromptTemplateSelectionFunc is a package-level function variable for test overriding.
35-
var charmPromptTemplateSelectionFunc = charmPromptTemplateSelection
34+
// runForm executes a huh form. It is a package-level variable so tests can
35+
// override the interactive terminal dependency while testing the surrounding logic.
36+
var runForm = func(f *huh.Form) error { return f.Run() }
3637

3738
// buildTemplateSelectionForm constructs a single-screen huh form where the category
3839
// and template selects are in the same group. Changing the category dynamically
@@ -84,7 +85,7 @@ func charmPromptTemplateSelection(ctx context.Context, clients *shared.ClientFac
8485

8586
var category string
8687
var template string
87-
err := buildTemplateSelectionForm(clients, &category, &template).Run()
88+
err := runForm(buildTemplateSelectionForm(clients, &category, &template))
8889
if err != nil {
8990
return templateSelectionResult{}, slackerror.ToSlackError(err)
9091
}

cmd/project/create_template_charm_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
package project
1616

1717
import (
18+
"context"
19+
"fmt"
1820
"testing"
1921

2022
tea "github.com/charmbracelet/bubbletea"
2123
"github.com/charmbracelet/huh"
2224
"github.com/charmbracelet/x/ansi"
2325
"github.com/slackapi/slack-cli/internal/shared"
2426
"github.com/stretchr/testify/assert"
27+
"github.com/stretchr/testify/require"
2528
)
2629

2730
// doAllUpdates recursively processes all commands returned by form updates,
@@ -160,3 +163,96 @@ func TestBuildTemplateSelectionForm(t *testing.T) {
160163
assert.Contains(t, view, "┃")
161164
})
162165
}
166+
167+
func TestCharmPromptTemplateSelection(t *testing.T) {
168+
originalRunForm := runForm
169+
t.Cleanup(func() { runForm = originalRunForm })
170+
171+
t.Run("returns selected category and template", func(t *testing.T) {
172+
cm := shared.NewClientsMock()
173+
cm.AddDefaultMocks()
174+
clients := shared.NewClientFactory(cm.MockClientFactory())
175+
176+
runForm = func(f *huh.Form) error {
177+
doAllUpdates(f, f.Init())
178+
// Select first category (Starter app)
179+
_, cmd := f.Update(tea.KeyMsg{Type: tea.KeyEnter})
180+
doAllUpdates(f, cmd)
181+
// Select first template (Bolt for JavaScript)
182+
_, cmd = f.Update(tea.KeyMsg{Type: tea.KeyEnter})
183+
doAllUpdates(f, cmd)
184+
return nil
185+
}
186+
187+
result, err := charmPromptTemplateSelection(context.Background(), clients)
188+
require.NoError(t, err)
189+
assert.Equal(t, "slack-cli#getting-started", result.CategoryID)
190+
assert.Equal(t, "slack-samples/bolt-js-starter-template", result.TemplateRepo)
191+
})
192+
193+
t.Run("returns error when form fails", func(t *testing.T) {
194+
cm := shared.NewClientsMock()
195+
cm.AddDefaultMocks()
196+
clients := shared.NewClientFactory(cm.MockClientFactory())
197+
198+
runForm = func(f *huh.Form) error {
199+
return fmt.Errorf("user cancelled")
200+
}
201+
202+
_, err := charmPromptTemplateSelection(context.Background(), clients)
203+
require.Error(t, err)
204+
assert.Contains(t, err.Error(), "user cancelled")
205+
})
206+
207+
t.Run("returns view more samples selection", func(t *testing.T) {
208+
cm := shared.NewClientsMock()
209+
cm.AddDefaultMocks()
210+
clients := shared.NewClientFactory(cm.MockClientFactory())
211+
212+
runForm = func(f *huh.Form) error {
213+
doAllUpdates(f, f.Init())
214+
// Navigate to "View more samples" (4th option)
215+
_, cmd := f.Update(tea.KeyMsg{Type: tea.KeyDown})
216+
doAllUpdates(f, cmd)
217+
_, cmd = f.Update(tea.KeyMsg{Type: tea.KeyDown})
218+
doAllUpdates(f, cmd)
219+
_, cmd = f.Update(tea.KeyMsg{Type: tea.KeyDown})
220+
doAllUpdates(f, cmd)
221+
_, cmd = f.Update(tea.KeyMsg{Type: tea.KeyEnter})
222+
doAllUpdates(f, cmd)
223+
// Select "Browse sample gallery..."
224+
_, cmd = f.Update(tea.KeyMsg{Type: tea.KeyEnter})
225+
doAllUpdates(f, cmd)
226+
return nil
227+
}
228+
229+
result, err := charmPromptTemplateSelection(context.Background(), clients)
230+
require.NoError(t, err)
231+
assert.Equal(t, viewMoreSamples, result.CategoryID)
232+
assert.Equal(t, viewMoreSamples, result.TemplateRepo)
233+
})
234+
235+
t.Run("selects AI agent category and template", func(t *testing.T) {
236+
cm := shared.NewClientsMock()
237+
cm.AddDefaultMocks()
238+
clients := shared.NewClientFactory(cm.MockClientFactory())
239+
240+
runForm = func(f *huh.Form) error {
241+
doAllUpdates(f, f.Init())
242+
// Navigate to "AI Agent app" (2nd option)
243+
_, cmd := f.Update(tea.KeyMsg{Type: tea.KeyDown})
244+
doAllUpdates(f, cmd)
245+
_, cmd = f.Update(tea.KeyMsg{Type: tea.KeyEnter})
246+
doAllUpdates(f, cmd)
247+
// Select first template (Bolt for JavaScript)
248+
_, cmd = f.Update(tea.KeyMsg{Type: tea.KeyEnter})
249+
doAllUpdates(f, cmd)
250+
return nil
251+
}
252+
253+
result, err := charmPromptTemplateSelection(context.Background(), clients)
254+
require.NoError(t, err)
255+
assert.Equal(t, "slack-cli#ai-apps", result.CategoryID)
256+
assert.Equal(t, "slack-samples/bolt-js-assistant-template", result.TemplateRepo)
257+
})
258+
}

cmd/project/create_test.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ package project
1616

1717
import (
1818
"context"
19+
"fmt"
1920
"testing"
2021

22+
tea "github.com/charmbracelet/bubbletea"
23+
"github.com/charmbracelet/huh"
2124
"github.com/slackapi/slack-cli/internal/config"
2225
"github.com/slackapi/slack-cli/internal/experiment"
2326
"github.com/slackapi/slack-cli/internal/iostreams"
@@ -535,19 +538,22 @@ func TestCreateCommand(t *testing.T) {
535538
// Enable the charm experiment
536539
cm.Config.ExperimentsFlag = []string{string(experiment.Charm)}
537540
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
538-
// Override the charm prompt function
539-
charmPromptTemplateSelectionFunc = func(_ context.Context, _ *shared.ClientFactory) (templateSelectionResult, error) {
540-
return templateSelectionResult{
541-
CategoryID: "slack-cli#getting-started",
542-
TemplateRepo: "slack-samples/bolt-js-starter-template",
543-
}, nil
541+
// Override runForm to simulate form completion without a terminal
542+
runForm = func(f *huh.Form) error {
543+
doAllUpdates(f, f.Init())
544+
// Select first category (Starter app) then first template (Bolt for JS)
545+
_, cmd := f.Update(tea.KeyMsg{Type: tea.KeyEnter})
546+
doAllUpdates(f, cmd)
547+
_, cmd = f.Update(tea.KeyMsg{Type: tea.KeyEnter})
548+
doAllUpdates(f, cmd)
549+
return nil
544550
}
545551
createClientMock = new(CreateClientMock)
546552
createClientMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil)
547553
CreateFunc = createClientMock.Create
548554
},
549555
Teardown: func() {
550-
charmPromptTemplateSelectionFunc = charmPromptTemplateSelection
556+
runForm = func(f *huh.Form) error { return f.Run() }
551557
},
552558
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
553559
template, err := create.ResolveTemplateURL("slack-samples/bolt-js-starter-template")
@@ -561,6 +567,29 @@ func TestCreateCommand(t *testing.T) {
561567
cm.IO.AssertNotCalled(t, "SelectPrompt", mock.Anything, "Select an app:", mock.Anything, mock.Anything)
562568
},
563569
},
570+
"charm dynamic form returns error": {
571+
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
572+
cm.AddDefaultMocks()
573+
cm.IO.On("IsTTY").Unset()
574+
cm.IO.On("IsTTY").Return(true)
575+
// Enable the charm experiment
576+
cm.Config.ExperimentsFlag = []string{string(experiment.Charm)}
577+
cm.Config.LoadExperiments(ctx, cm.IO.PrintDebug)
578+
// Override runForm to return an error
579+
runForm = func(f *huh.Form) error {
580+
return fmt.Errorf("user cancelled")
581+
}
582+
createClientMock = new(CreateClientMock)
583+
CreateFunc = createClientMock.Create
584+
},
585+
Teardown: func() {
586+
runForm = func(f *huh.Form) error { return f.Run() }
587+
},
588+
ExpectedErrorStrings: []string{"user cancelled"},
589+
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
590+
createClientMock.AssertNotCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
591+
},
592+
},
564593
"lists agent templates with agent --list flag": {
565594
CmdArgs: []string{"agent", "--list"},
566595
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {

0 commit comments

Comments
 (0)