@@ -224,6 +224,217 @@ func TestAccessiblePrompter(t *testing.T) {
224224 assert .Equal (t , []int {1 }, multiSelectValues )
225225 })
226226
227+ t .Run ("MultiSelectWithSearch - basic flow" , func (t * testing.T ) {
228+ console := newTestVirtualTerminal (t )
229+ p := newTestAccessiblePrompter (t , console )
230+ persistentOptions := []string {"persistent-option-1" }
231+ searchFunc := func (input string ) prompter.MultiSelectSearchResult {
232+ var searchResultKeys []string
233+ var searchResultLabels []string
234+
235+ // Initial search with no input
236+ if input == "" {
237+ moreResults := 2
238+ searchResultKeys = []string {"initial-result-1" , "initial-result-2" }
239+ searchResultLabels = []string {"Initial Result Label 1" , "Initial Result Label 2" }
240+ return prompter.MultiSelectSearchResult {
241+ Keys : searchResultKeys ,
242+ Labels : searchResultLabels ,
243+ MoreResults : moreResults ,
244+ Err : nil ,
245+ }
246+ }
247+
248+ // Subsequent search with input
249+ moreResults := 0
250+ searchResultKeys = []string {"search-result-1" , "search-result-2" }
251+ searchResultLabels = []string {"Search Result Label 1" , "Search Result Label 2" }
252+ return prompter.MultiSelectSearchResult {
253+ Keys : searchResultKeys ,
254+ Labels : searchResultLabels ,
255+ MoreResults : moreResults ,
256+ Err : nil ,
257+ }
258+ }
259+
260+ go func () {
261+ // Wait for prompt to appear
262+ _ , err := console .ExpectString ("Select an option \r \n " )
263+ require .NoError (t , err )
264+
265+ // Select the search option, which will always be the first option
266+ _ , err = console .SendLine ("1" )
267+ require .NoError (t , err )
268+
269+ // Submit search
270+ _ , err = console .SendLine ("0" )
271+ require .NoError (t , err )
272+
273+ // Wait for the search prompt to appear
274+ _ , err = console .ExpectString ("Search for an option" )
275+ require .NoError (t , err )
276+
277+ // Enter some search text to trigger the search
278+ _ , err = console .SendLine ("search text" )
279+ require .NoError (t , err )
280+
281+ // Wait for the multiselect prompt to re-appear after search
282+ _ , err = console .ExpectString ("Select an option \r \n " )
283+ require .NoError (t , err )
284+
285+ // Select the first search result
286+ _ , err = console .SendLine ("2" )
287+ require .NoError (t , err )
288+
289+ // This confirms selections
290+ _ , err = console .SendLine ("0" )
291+ require .NoError (t , err )
292+ }()
293+ multiSelectValues , err := p .MultiSelectWithSearch ("Select an option" , "Search for an option" , []string {}, persistentOptions , searchFunc )
294+ require .NoError (t , err )
295+ assert .Equal (t , []string {"search-result-1" }, multiSelectValues )
296+ })
297+
298+ t .Run ("MultiSelectWithSearch - defaults are pre-selected" , func (t * testing.T ) {
299+ console := newTestVirtualTerminal (t )
300+ p := newTestAccessiblePrompter (t , console )
301+ initialSearchResultKeys := []string {"initial-result-1" }
302+ initialSearchResultLabels := []string {"Initial Result Label 1" }
303+ defaultOptions := initialSearchResultKeys
304+ searchFunc := func (input string ) prompter.MultiSelectSearchResult {
305+ // Initial search with no input
306+ if input == "" {
307+ moreResults := 2
308+ return prompter.MultiSelectSearchResult {
309+ Keys : initialSearchResultKeys ,
310+ Labels : initialSearchResultLabels ,
311+ MoreResults : moreResults ,
312+ Err : nil ,
313+ }
314+ }
315+
316+ // No search selected, so this should fail the test.
317+ t .FailNow ()
318+ return prompter.MultiSelectSearchResult {
319+ Keys : nil ,
320+ Labels : nil ,
321+ MoreResults : 0 ,
322+ Err : nil ,
323+ }
324+ }
325+
326+ go func () {
327+ // Wait for prompt to appear
328+ _ , err := console .ExpectString ("Select an option (default: Initial Result Label 1) \r \n " )
329+ require .NoError (t , err )
330+
331+ // This confirms default selections
332+ _ , err = console .SendLine ("0" )
333+ require .NoError (t , err )
334+ }()
335+ multiSelectValues , err := p .MultiSelectWithSearch ("Select an option" , "Search for an option" , defaultOptions , initialSearchResultKeys , searchFunc )
336+ require .NoError (t , err )
337+ assert .Equal (t , defaultOptions , multiSelectValues )
338+ })
339+
340+ t .Run ("MultiSelectWithSearch - selected options persist between searches" , func (t * testing.T ) {
341+ console := newTestVirtualTerminal (t )
342+ p := newTestAccessiblePrompter (t , console )
343+ initialSearchResultKeys := []string {"initial-result-1" }
344+ initialSearchResultLabels := []string {"Initial Result Label 1" }
345+ moreResultKeys := []string {"more-result-1" }
346+ moreResultLabels := []string {"More Result Label 1" }
347+
348+ searchFunc := func (input string ) prompter.MultiSelectSearchResult {
349+ // Initial search with no input
350+ if input == "" {
351+ moreResults := 2
352+ return prompter.MultiSelectSearchResult {
353+ Keys : initialSearchResultKeys ,
354+ Labels : initialSearchResultLabels ,
355+ MoreResults : moreResults ,
356+ Err : nil ,
357+ }
358+ }
359+
360+ // Subsequent search with input "more"
361+ if input == "more" {
362+ return prompter.MultiSelectSearchResult {
363+ Keys : moreResultKeys ,
364+ Labels : moreResultLabels ,
365+ MoreResults : 0 ,
366+ Err : nil ,
367+ }
368+ }
369+
370+ // No other searches expected
371+ t .FailNow ()
372+ return prompter.MultiSelectSearchResult {
373+ Keys : nil ,
374+ Labels : nil ,
375+ MoreResults : 0 ,
376+ Err : nil ,
377+ }
378+ }
379+
380+ go func () {
381+ // Wait for prompt to appear
382+ _ , err := console .ExpectString ("Select an option \r \n " )
383+ require .NoError (t , err )
384+
385+ // Select one of our initial search results
386+ _ , err = console .SendLine ("2" )
387+ require .NoError (t , err )
388+
389+ // Select to search
390+ _ , err = console .SendLine ("1" )
391+ require .NoError (t , err )
392+
393+ // Submit the search selection
394+ _ , err = console .SendLine ("0" )
395+ require .NoError (t , err )
396+
397+ // Wait for the search prompt to appear
398+ _ , err = console .ExpectString ("Search for an option" )
399+ require .NoError (t , err )
400+
401+ // Enter some search text to trigger the search
402+ _ , err = console .SendLine ("more" )
403+ require .NoError (t , err )
404+
405+ // Wait for the multiselect prompt to re-appear after search
406+ _ , err = console .ExpectString ("Select up to" )
407+ require .NoError (t , err )
408+
409+ // Select the new option from the new search results
410+ _ , err = console .SendLine ("3" )
411+ require .NoError (t , err )
412+
413+ // Submit selections
414+ _ , err = console .SendLine ("0" )
415+ require .NoError (t , err )
416+ }()
417+ multiSelectValues , err := p .MultiSelectWithSearch ("Select an option" , "Search for an option" , []string {}, []string {}, searchFunc )
418+ require .NoError (t , err )
419+ expectedValues := append (initialSearchResultKeys , moreResultKeys ... )
420+ assert .Equal (t , expectedValues , multiSelectValues )
421+ })
422+
423+ t .Run ("MultiSelectWithSearch - search error propagates" , func (t * testing.T ) {
424+ console := newTestVirtualTerminal (t )
425+ p := newTestAccessiblePrompter (t , console )
426+
427+ searchFunc := func (input string ) prompter.MultiSelectSearchResult {
428+ return prompter.MultiSelectSearchResult {
429+ Err : fmt .Errorf ("search error" ),
430+ }
431+ }
432+
433+ _ , err := p .MultiSelectWithSearch ("Select" , "Search" , []string {}, []string {}, searchFunc )
434+ require .Error (t , err )
435+ require .Contains (t , err .Error (), "search error" )
436+ })
437+
227438 t .Run ("Input" , func (t * testing.T ) {
228439 console := newTestVirtualTerminal (t )
229440 p := newTestAccessiblePrompter (t , console )
@@ -642,6 +853,9 @@ func newTestVirtualTerminal(t *testing.T) *expect.Console {
642853 failOnExpectError (t ),
643854 failOnSendError (t ),
644855 expect .WithDefaultTimeout (time .Second ),
856+ // Use this logger to debug expect based tests by printing the
857+ // characters being read to stdout.
858+ // expect.WithLogger(log.New(os.Stdout, "", 0)),
645859 }
646860
647861 console , err := expect .NewConsole (consoleOpts ... )
0 commit comments