@@ -2,6 +2,9 @@ package cmd
22
33import (
44 "errors"
5+ "io"
6+ "os"
7+ "strings"
58 "testing"
69
710 "github.com/git-hulk/clime/internal/prompt"
@@ -96,15 +99,20 @@ func TestRunInteractiveSkillsInstallEscAtTopLevelKeepsUIOpen(t *testing.T) {
9699 return nil
97100 }
98101
99- if err := runInteractiveSkillsInstall (manifest ); err != nil {
100- t .Fatalf ("runInteractiveSkillsInstall() error = %v" , err )
102+ output := captureStdout (t , func () {
103+ if err := runInteractiveSkillsInstall (manifest ); err != nil {
104+ t .Fatalf ("runInteractiveSkillsInstall() error = %v" , err )
105+ }
106+ })
107+ if output != "\n " {
108+ t .Fatalf ("stdout = %q, want %q" , output , "\n " )
101109 }
102110 if ! called {
103111 t .Fatal ("expected skillsActionRunner to be called" )
104112 }
105113}
106114
107- func TestRunInteractiveSkillsInstallEscFromInstallReturnsToActionMenu (t * testing.T ) {
115+ func TestRunInteractiveSkillsInstallEscFromInstallDoesNotPrintExtraSpacer (t * testing.T ) {
108116 manifest := & skill.Manifest {
109117 Skills : []skill.InstalledSkill {
110118 {Name : "alpha" , Source : "owner/repo" },
@@ -121,7 +129,7 @@ func TestRunInteractiveSkillsInstallEscFromInstallReturnsToActionMenu(t *testing
121129 case 1 :
122130 return 0 , nil
123131 case 2 :
124- return 0 , nil
132+ return 1 , nil
125133 case 3 :
126134 return 1 , nil
127135 default :
@@ -130,23 +138,25 @@ func TestRunInteractiveSkillsInstallEscFromInstallReturnsToActionMenu(t *testing
130138 }
131139 }
132140
133- var actions [] sourceAction
141+ runs := 0
134142 skillsActionRunner = func (manifest * skill.Manifest , repo string , action sourceAction ) error {
135- actions = append ( actions , action )
136- if len ( actions ) == 1 {
143+ runs ++
144+ if runs == 1 {
137145 return prompt .ErrBack
138146 }
139147 return nil
140148 }
141149
142- if err := runInteractiveSkillsInstall (manifest ); err != nil {
143- t .Fatalf ("runInteractiveSkillsInstall() error = %v" , err )
150+ output := captureStdout (t , func () {
151+ if err := runInteractiveSkillsInstall (manifest ); err != nil {
152+ t .Fatalf ("runInteractiveSkillsInstall() error = %v" , err )
153+ }
154+ })
155+ if output != "\n \n " {
156+ t .Fatalf ("stdout = %q, want %q" , output , "\n \n " )
144157 }
145- if len (actions ) != 2 {
146- t .Fatalf ("actions length = %d, want 2" , len (actions ))
147- }
148- if actions [0 ] != actionBrowseInstall || actions [1 ] != actionUpdate {
149- t .Fatalf ("actions = %v, want [%v %v]" , actions , actionBrowseInstall , actionUpdate )
158+ if runs != 2 {
159+ t .Fatalf ("skillsActionRunner calls = %d, want 2" , runs )
150160 }
151161}
152162
@@ -169,8 +179,16 @@ func TestInteractiveUninstallEscKeepsMenuOpen(t *testing.T) {
169179 return nil , nil
170180 }
171181
172- if err := interactiveUninstall (manifest ); err != nil {
173- t .Fatalf ("interactiveUninstall() error = %v" , err )
182+ output := captureStdout (t , func () {
183+ if err := interactiveUninstall (manifest ); err != nil {
184+ t .Fatalf ("interactiveUninstall() error = %v" , err )
185+ }
186+ })
187+ if ! strings .HasPrefix (output , "\n " ) {
188+ t .Fatalf ("stdout = %q, want prefix %q" , output , "\n " )
189+ }
190+ if strings .HasPrefix (output , "\n \n " ) {
191+ t .Fatalf ("stdout = %q, want a single leading spacer line" , output )
174192 }
175193 if calls != 2 {
176194 t .Fatalf ("multiSelectPrompt calls = %d, want 2" , calls )
@@ -197,6 +215,83 @@ func TestInteractiveUninstallInterruptPropagates(t *testing.T) {
197215 }
198216}
199217
218+ func captureStdout (t * testing.T , fn func ()) string {
219+ t .Helper ()
220+
221+ origStdout := os .Stdout
222+ r , w , err := os .Pipe ()
223+ if err != nil {
224+ t .Fatalf ("os.Pipe() error = %v" , err )
225+ }
226+ os .Stdout = w
227+
228+ done := make (chan string , 1 )
229+ go func () {
230+ data , _ := io .ReadAll (r )
231+ done <- string (data )
232+ }()
233+
234+ defer func () {
235+ os .Stdout = origStdout
236+ }()
237+
238+ fn ()
239+
240+ if err := w .Close (); err != nil {
241+ t .Fatalf ("stdout close error = %v" , err )
242+ }
243+ output := <- done
244+ if err := r .Close (); err != nil {
245+ t .Fatalf ("stdout reader close error = %v" , err )
246+ }
247+ return output
248+ }
249+ func TestRunInteractiveSkillsInstallEscFromInstallReturnsToActionMenu (t * testing.T ) {
250+ manifest := & skill.Manifest {
251+ Skills : []skill.InstalledSkill {
252+ {Name : "alpha" , Source : "owner/repo" },
253+ },
254+ }
255+
256+ restore := stubSkillPrompts (t )
257+ defer restore ()
258+
259+ selectCalls := 0
260+ selectPrompt = func (config prompt.SelectConfig ) (int , error ) {
261+ selectCalls ++
262+ switch selectCalls {
263+ case 1 :
264+ return 0 , nil
265+ case 2 :
266+ return 0 , nil
267+ case 3 :
268+ return 1 , nil
269+ default :
270+ t .Fatalf ("unexpected select call %d" , selectCalls )
271+ return 0 , nil
272+ }
273+ }
274+
275+ var actions []sourceAction
276+ skillsActionRunner = func (manifest * skill.Manifest , repo string , action sourceAction ) error {
277+ actions = append (actions , action )
278+ if len (actions ) == 1 {
279+ return prompt .ErrBack
280+ }
281+ return nil
282+ }
283+
284+ if err := runInteractiveSkillsInstall (manifest ); err != nil {
285+ t .Fatalf ("runInteractiveSkillsInstall() error = %v" , err )
286+ }
287+ if len (actions ) != 2 {
288+ t .Fatalf ("actions length = %d, want 2" , len (actions ))
289+ }
290+ if actions [0 ] != actionBrowseInstall || actions [1 ] != actionUpdate {
291+ t .Fatalf ("actions = %v, want [%v %v]" , actions , actionBrowseInstall , actionUpdate )
292+ }
293+ }
294+
200295func stubSkillPrompts (t * testing.T ) func () {
201296 t .Helper ()
202297
0 commit comments