spec/unit/multi_select_spec.rb in tty-prompt-0.18.1 vs spec/unit/multi_select_spec.rb in tty-prompt-0.19.0

- old
+ new

@@ -1,29 +1,33 @@ # frozen_string_literal: true RSpec.describe TTY::Prompt do let(:symbols) { TTY::Prompt::Symbols.symbols } + let(:up_down) { "#{symbols[:arrow_up]}/#{symbols[:arrow_down]}" } + let(:left_right) { "#{symbols[:arrow_left]}/#{symbols[:arrow_right]}"} def output_helper(prompt, choices, active, selected, options = {}) raise ":init requires :hint" if options[:init] && options[:hint].nil? hint = options[:hint] init = options.fetch(:init, false) enum = options[:enum] out = [] out << "\e[?25l" if init out << prompt << " " + out << "(max. #{options[:max]}) " if options[:max] out << selected.join(', ') + out << " " if init && !selected.empty? out << "\e[90m" if init out << (init ? "(#{hint})\e[0m" : " (#{hint})") if hint out << "\n" out << choices.map.with_index do |choice, i| name = choice.is_a?(Hash) ? choice[:name] : choice disabled = choice.is_a?(Hash) ? choice[:disabled] : false num = (i + 1).to_s + enum if enum - prefix = name == active ? "#{symbols[:pointer]} " : " " + prefix = name == active ? "#{symbols[:marker]} " : " " prefix += if disabled "\e[31m#{symbols[:cross]}\e[0m #{num}#{name} #{disabled}" elsif selected.include?(name) "\e[32m#{symbols[:radio_on]}\e[0m #{num}#{name}" else @@ -46,125 +50,134 @@ it "selects nothing when return pressed immediately" do prompt = TTY::TestPrompt.new choices = %i(vodka beer wine whisky bourbon) prompt.input << "\r" prompt.input.rewind + expect(prompt.multi_select("Select drinks?", choices)). to eq([]) - expect(prompt.output.string).to eq([ - "\e[?25lSelect drinks? \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n", - "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n", + + expected_output = [ + "\e[?25lSelect drinks? \e[90m(Use #{up_down} arrow keys, press Space to select and Enter to finish)\e[0m\n", + "#{symbols[:marker]} #{symbols[:radio_off]} vodka\n", " #{symbols[:radio_off]} beer\n", " #{symbols[:radio_off]} wine\n", " #{symbols[:radio_off]} whisky\n", " #{symbols[:radio_off]} bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", "Select drinks? \n\e[?25h" - ].join) + ].join + expect(prompt.output.string).to eq(expected_output) end it "selects item when space pressed" do prompt = TTY::TestPrompt.new choices = %w(vodka beer wine whisky bourbon) prompt.input << " \r" prompt.input.rewind expect(prompt.multi_select("Select drinks?", choices)). to eq(['vodka']) - expect(prompt.output.string).to eq([ - "\e[?25lSelect drinks? \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n", - "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n", + + expected_output = [ + "\e[?25lSelect drinks? \e[90m(Use #{up_down} arrow keys, press Space to select and Enter to finish)\e[0m\n", + "#{symbols[:marker]} #{symbols[:radio_off]} vodka\n", " #{symbols[:radio_off]} beer\n", " #{symbols[:radio_off]} wine\n", " #{symbols[:radio_off]} whisky\n", " #{symbols[:radio_off]} bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", "Select drinks? vodka\n", - "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m vodka\n", + "#{symbols[:marker]} \e[32m#{symbols[:radio_on]}\e[0m vodka\n", " #{symbols[:radio_off]} beer\n", " #{symbols[:radio_off]} wine\n", " #{symbols[:radio_off]} whisky\n", " #{symbols[:radio_off]} bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", "Select drinks? \e[32mvodka\e[0m\n\e[?25h" - ].join) + ].join + expect(prompt.output.string).to eq(expected_output) end it "selects item when space pressed but doesn't echo item if echo: false" do prompt = TTY::TestPrompt.new choices = %w(vodka beer wine whisky bourbon) prompt.input << " \r" prompt.input.rewind expect(prompt.multi_select("Select drinks?", choices, echo: false)). to eq(['vodka']) - expect(prompt.output.string).to eq([ - "\e[?25lSelect drinks? \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n", - "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n", + + expected_output = [ + "\e[?25lSelect drinks? \e[90m(Use #{up_down} arrow keys, press Space to select and Enter to finish)\e[0m\n", + "#{symbols[:marker]} #{symbols[:radio_off]} vodka\n", " #{symbols[:radio_off]} beer\n", " #{symbols[:radio_off]} wine\n", " #{symbols[:radio_off]} whisky\n", " #{symbols[:radio_off]} bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", "Select drinks? \n", - "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m vodka\n", + "#{symbols[:marker]} \e[32m#{symbols[:radio_on]}\e[0m vodka\n", " #{symbols[:radio_off]} beer\n", " #{symbols[:radio_off]} wine\n", " #{symbols[:radio_off]} whisky\n", " #{symbols[:radio_off]} bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", "Select drinks? \n\e[?25h" - ].join) + ].join + expect(prompt.output.string).to eq(expected_output) end it "sets choice custom values" do prompt = TTY::TestPrompt.new choices = {vodka: 1, beer: 2, wine: 3, whisky: 4, bourbon: 5} prompt.input << " \r" prompt.input.rewind expect(prompt.multi_select("Select drinks?", choices)).to eq([1]) - expect(prompt.output.string).to eq([ - "\e[?25lSelect drinks? \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n", - "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n", + expected_output = [ + "\e[?25lSelect drinks? \e[90m(Use #{up_down} arrow keys, press Space to select and Enter to finish)\e[0m\n", + "#{symbols[:marker]} #{symbols[:radio_off]} vodka\n", " #{symbols[:radio_off]} beer\n", " #{symbols[:radio_off]} wine\n", " #{symbols[:radio_off]} whisky\n", " #{symbols[:radio_off]} bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", "Select drinks? vodka\n", - "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m vodka\n", + "#{symbols[:marker]} \e[32m#{symbols[:radio_on]}\e[0m vodka\n", " #{symbols[:radio_off]} beer\n", " #{symbols[:radio_off]} wine\n", " #{symbols[:radio_off]} whisky\n", " #{symbols[:radio_off]} bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", "Select drinks? \e[32mvodka\e[0m\n\e[?25h" - ].join) + ].join + expect(prompt.output.string).to eq(expected_output) end it "sets choice name and value through DSL" do prompt = TTY::TestPrompt.new prompt.input << " \r" prompt.input.rewind value = prompt.multi_select("Select drinks?") do |menu| + menu.symbols marker: '>', radio_off: '-', radio_on: '=' menu.enum ')' menu.choice :vodka, {score: 1} menu.choice :beer, 2 menu.choice :wine, 3 menu.choices whisky: 4, bourbon: 5 end expect(value).to eq([{score: 1}]) expect(prompt.output.string).to eq([ - "\e[?25lSelect drinks? \e[90m(Use arrow or number (1-5) keys, press Space to select and Enter to finish)\e[0m\n", - "#{symbols[:pointer]} #{symbols[:radio_off]} 1) vodka\n", - " #{symbols[:radio_off]} 2) beer\n", - " #{symbols[:radio_off]} 3) wine\n", - " #{symbols[:radio_off]} 4) whisky\n", - " #{symbols[:radio_off]} 5) bourbon", + "\e[?25lSelect drinks? \e[90m(Use #{up_down} arrow or number (1-5) keys, press Space to select and Enter to finish)\e[0m\n", + "> - 1) vodka\n", + " - 2) beer\n", + " - 3) wine\n", + " - 4) whisky\n", + " - 5) bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", "Select drinks? vodka\n", - "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m 1) vodka\n", - " #{symbols[:radio_off]} 2) beer\n", - " #{symbols[:radio_off]} 3) wine\n", - " #{symbols[:radio_off]} 4) whisky\n", - " #{symbols[:radio_off]} 5) bourbon", + "> \e[32m=\e[0m 1) vodka\n", + " - 2) beer\n", + " - 3) wine\n", + " - 4) whisky\n", + " - 5) bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", "Select drinks? \e[32mvodka\e[0m\n\e[?25h" ].join) end @@ -181,16 +194,16 @@ menu.choice :whisky, {score: 40} menu.choice :bourbon, {score: 50} end expect(value).to match_array([{score: 20}, {score: 50}]) expect(prompt.output.string).to eq([ - "\e[?25lSelect drinks? beer, bourbon \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n", + "\e[?25lSelect drinks? beer, bourbon \e[90m(Use #{up_down} arrow keys, press Space to select and Enter to finish)\e[0m\n", " #{symbols[:radio_off]} vodka\n", " \e[32m#{symbols[:radio_on]}\e[0m beer\n", " #{symbols[:radio_off]} wine\n", " #{symbols[:radio_off]} whisky\n", - "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m bourbon", + "#{symbols[:marker]} \e[32m#{symbols[:radio_on]}\e[0m bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", "Select drinks? \e[32mbeer, bourbon\e[0m\n\e[?25h", ].join) end @@ -229,12 +242,12 @@ choices = %w(vodka beer wine whisky bourbon) prompt.input << "\r" prompt.input.rewind expect(prompt.multi_select("Select drinks?", choices)). to eq([]) expect(prompt.output.string).to eq([ - "\e[?25l[?] Select drinks? \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n", - "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n", + "\e[?25l[?] Select drinks? \e[90m(Use #{up_down} arrow keys, press Space to select and Enter to finish)\e[0m\n", + "#{symbols[:marker]} #{symbols[:radio_off]} vodka\n", " #{symbols[:radio_off]} beer\n", " #{symbols[:radio_off]} wine\n", " #{symbols[:radio_off]} whisky\n", " #{symbols[:radio_off]} bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", @@ -245,14 +258,14 @@ it "changes selected item color & marker" do prompt = TTY::TestPrompt.new choices = %w(vodka beer wine whisky bourbon) prompt.input << "\r" prompt.input.rewind - options = {default: [1], active_color: :blue, marker: '>'} + options = {default: [1], active_color: :blue, symbols: {marker: '>'}} expect(prompt.multi_select("Select drinks?", choices, options)). to eq(['vodka']) expect(prompt.output.string).to eq([ - "\e[?25lSelect drinks? vodka \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n", + "\e[?25lSelect drinks? vodka \e[90m(Use #{up_down} arrow keys, press Space to select and Enter to finish)\e[0m\n", "> \e[34m#{symbols[:radio_on]}\e[0m vodka\n", " #{symbols[:radio_off]} beer\n", " #{symbols[:radio_off]} wine\n", " #{symbols[:radio_off]} whisky\n", " #{symbols[:radio_off]} bourbon", @@ -264,99 +277,221 @@ it "changes help text" do prompt = TTY::TestPrompt.new choices = %w(vodka beer wine whisky bourbon) prompt.input << "\r" prompt.input.rewind - expect(prompt.multi_select("Select drinks?", choices, help: '(Bash keyboard)')). to eq([]) - expect(prompt.output.string).to eq([ + + answer = prompt.multi_select("Select drinks?", choices, help: '(Bash keyboard)') + + expect(answer).to eq([]) + expected_output = [ "\e[?25lSelect drinks? \e[90m(Bash keyboard)\e[0m\n", - "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n", + "#{symbols[:marker]} #{symbols[:radio_off]} vodka\n", " #{symbols[:radio_off]} beer\n", " #{symbols[:radio_off]} wine\n", " #{symbols[:radio_off]} whisky\n", " #{symbols[:radio_off]} bourbon", "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G", "Select drinks? \n\e[?25h" - ].join) + ].join + expect(prompt.output.string).to eq(expected_output) end - it "paginates long selections" do - prompt = TTY::TestPrompt.new - choices = %w(A B C D E F G H) - prompt.input << "\r" - prompt.input.rewind - value = prompt.multi_select("What letter?", choices, per_page: 3, default: 4) - expect(value).to eq(['D']) - expect(prompt.output.string).to eq([ - "\e[?25lWhat letter? D \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n", - "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m D\n", - " #{symbols[:radio_off]} E\n", - " #{symbols[:radio_off]} F\n", - "\e[90m(Move up or down to reveal more choices)\e[0m", - "\e[2K\e[1G\e[1A" * 4, "\e[2K\e[1G", - "What letter? \e[32mD\e[0m\n\e[?25h", - ].join) - end + context "when paginated" do + it "paginates long selections" do + prompt = TTY::TestPrompt.new + choices = %w(A B C D E F G H) + prompt.input << "\r" + prompt.input.rewind - it "paginates choices as hash object" do - prompt = TTY::TestPrompt.new - choices = {A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8} - prompt.input << "\r" - prompt.input.rewind - value = prompt.multi_select("What letter?", choices, default: 4, per_page: 3) - expect(value).to eq([4]) - expect(prompt.output.string).to eq([ - "\e[?25lWhat letter? D \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n", - "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m D\n", - " #{symbols[:radio_off]} E\n", - " #{symbols[:radio_off]} F\n", - "\e[90m(Move up or down to reveal more choices)\e[0m", - "\e[2K\e[1G\e[1A" * 4, "\e[2K\e[1G", - "What letter? \e[32mD\e[0m\n\e[?25h", - ].join) - end + answer = prompt.multi_select("What letter?", choices, per_page: 3, default: 4) - it "paginates long selections through DSL" do - prompt = TTY::TestPrompt.new - choices = %w(A B C D E F G H) - prompt.input << "\r" - prompt.input.rewind - value = prompt.multi_select("What letter?") do |menu| - menu.per_page 3 - menu.page_help '(Wiggle thy finger up or down to see more)' - menu.default 4 - menu.choices choices - end - expect(value).to eq(['D']) - expect(prompt.output.string).to eq([ - "\e[?25lWhat letter? D \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n", - "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m D\n", - " #{symbols[:radio_off]} E\n", - " #{symbols[:radio_off]} F\n", - "\e[90m(Wiggle thy finger up or down to see more)\e[0m", - "\e[2K\e[1G\e[1A" * 4, "\e[2K\e[1G", - "What letter? \e[32mD\e[0m\n\e[?25h", - ].join) - end + expect(answer).to eq(['D']) + expected_output = [ + "\e[?25lWhat letter? D \e[90m(Use #{up_down} and #{left_right} arrow keys, press Space to select and Enter to finish)\e[0m\n", + "#{symbols[:marker]} \e[32m#{symbols[:radio_on]}\e[0m D\n", + " #{symbols[:radio_off]} E\n", + " #{symbols[:radio_off]} F", + "\e[2K\e[1G\e[1A" * 3, + "\e[2K\e[1G", + "What letter? \e[32mD\e[0m\n\e[?25h", + ].join + expect(prompt.output.string).to eq(expected_output) + end - it "doesn't paginate short selections" do - prompt = TTY::TestPrompt.new - choices = %w(A B C D) - prompt.input << "\r" - prompt.input.rewind - value = prompt.multi_select("What letter?", choices, per_page: 4, default: 1) - expect(value).to eq(['A']) + it "paginates choices as hash object" do + prompt = TTY::TestPrompt.new + choices = {A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8} + prompt.input << "\r" + prompt.input.rewind - expect(prompt.output.string).to eq([ - "\e[?25lWhat letter? A \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n", - "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m A\n", - " #{symbols[:radio_off]} B\n", - " #{symbols[:radio_off]} C\n", - " #{symbols[:radio_off]} D", - "\e[2K\e[1G\e[1A" * 4, "\e[2K\e[1G", - "What letter? \e[32mA\e[0m\n\e[?25h", - ].join) + answer = prompt.multi_select("What letter?", choices, default: 4, per_page: 3) + + expect(answer).to eq([4]) + expected_output = [ + "\e[?25lWhat letter? D ", + "\e[90m(Use #{up_down} and #{left_right} arrow keys, press Space to select and Enter to finish)\e[0m\n", + "#{symbols[:marker]} \e[32m#{symbols[:radio_on]}\e[0m D\n", + " #{symbols[:radio_off]} E\n", + " #{symbols[:radio_off]} F", + "\e[2K\e[1G\e[1A" * 3, "\e[2K\e[1G", + "What letter? \e[32mD\e[0m\n\e[?25h", + ].join + expect(prompt.output.string).to eq(expected_output) + end + + it "paginates long selections through DSL" do + prompt = TTY::TestPrompt.new + choices = %w(A B C D E F G H) + prompt.input << "\r" + prompt.input.rewind + + answer = prompt.multi_select("What letter?") do |menu| + menu.per_page 3 + menu.default 4 + menu.choices choices + end + + expect(answer).to eq(['D']) + expected_output = [ + "\e[?25lWhat letter? D ", + "\e[90m(Use #{up_down} and #{left_right} arrow keys, press Space to select and Enter to finish)\e[0m\n", + "#{symbols[:marker]} \e[32m#{symbols[:radio_on]}\e[0m D\n", + " #{symbols[:radio_off]} E\n", + " #{symbols[:radio_off]} F", + "\e[2K\e[1G\e[1A" * 3, + "\e[2K\e[1G", + "What letter? \e[32mD\e[0m\n\e[?25h", + ].join + expect(prompt.output.string).to eq(expected_output) + end + + it "doesn't paginate short selections" do + prompt = TTY::TestPrompt.new + choices = %w(A B C D) + prompt.input << "\r" + prompt.input.rewind + value = prompt.multi_select("What letter?", choices, per_page: 4, default: 1) + expect(value).to eq(['A']) + + expect(prompt.output.string).to eq([ + "\e[?25lWhat letter? A ", + "\e[90m(Use #{up_down} arrow keys, press Space to select and Enter to finish)\e[0m\n", + "#{symbols[:marker]} \e[32m#{symbols[:radio_on]}\e[0m A\n", + " #{symbols[:radio_off]} B\n", + " #{symbols[:radio_off]} C\n", + " #{symbols[:radio_off]} D", + "\e[2K\e[1G\e[1A" * 4, "\e[2K\e[1G", + "What letter? \e[32mA\e[0m\n\e[?25h", + ].join) + end + + it "navigates evenly paged output with right arrow until end of selection" do + prompt = TTY::TestPrompt.new + choices = ('1'..'12').to_a + prompt.on(:keypress) { |e| prompt.trigger(:keyright) if e.value == "l" } + prompt.input << "l" << "l" << "l" << " " << "\r" + prompt.input.rewind + + answer = prompt.multi_select("What number?", choices, per_page: 4) + + expect(answer).to eq(["9"]) + + expected_output = [ + output_helper("What number?", choices[0..3], "1", [], init: true, + hint: "Use #{up_down} and #{left_right} arrow keys, press Space to select and Enter to finish"), + output_helper("What number?", choices[4..7], "5", []), + output_helper("What number?", choices[8..11], "9", []), + output_helper("What number?", choices[8..11], "9", []), + output_helper("What number?", choices[8..11], "9", ["9"]), + "What number? \e[32m9\e[0m\n\e[?25h", + ].join + + expect(prompt.output.string).to eq(expected_output) + end + + it "navigates unevenly paged output with right arrow until the end of selection" do + prompt = TTY::TestPrompt.new + choices = ('1'..'10').to_a + prompt.on(:keypress) { |e| prompt.trigger(:keyright) if e.value == "l" } + prompt.input << "l" << "l" << "l" << " " << "\r" + prompt.input.rewind + + answer = prompt.multi_select("What number?", choices, default: 4, per_page: 4) + + expect(answer).to eq(['4', '10']) + + expected_output = [ + output_helper("What number?", choices[3..6], "4", ["4"], init: true, + hint: "Use #{up_down} and #{left_right} arrow keys, press Space to select and Enter to finish"), + output_helper("What number?", choices[4..7], "8", ["4"]), + output_helper("What number?", choices[8..9], "10", ["4"]), + output_helper("What number?", choices[8..9], "10", ["4"]), + output_helper("What number?", choices[8..9], "10", ["4", "10"]), + "What number? \e[32m4, 10\e[0m\n\e[?25h", + ].join + + expect(prompt.output.string).to eq(expected_output) + end + + it "navigates left and right" do + prompt = TTY::TestPrompt.new + choices = ('1'..'10').to_a + prompt.on(:keypress) { |e| + prompt.trigger(:keyright) if e.value == "l" + prompt.trigger(:keyleft) if e.value == "h" + } + prompt.input << "l" << "l" << "h" << " " << "\r" + prompt.input.rewind + + answer = prompt.multi_select("What number?", choices, default: 2, per_page: 4) + + expect(answer).to eq(['2', '6']) + + expected_output = [ + output_helper("What number?", choices[0..3], "2", ["2"], init: true, + hint: "Use #{up_down} and #{left_right} arrow keys, press Space to select and Enter to finish"), + output_helper("What number?", choices[4..7], "6", ["2"]), + output_helper("What number?", choices[8..9], "10", ["2"]), + output_helper("What number?", choices[4..7], "6", ["2"]), + output_helper("What number?", choices[4..7], "6", ["2", "6"]), + "What number? \e[32m2, 6\e[0m\n\e[?25h", + ].join + + expect(prompt.output.string).to eq(expected_output) + end + + it "combines up/down navigation with left/right" do + prompt = TTY::TestPrompt.new + choices = ('1'..'11').to_a + prompt.on(:keypress) { |e| + prompt.trigger(:keyup) if e.value == "k" + prompt.trigger(:keydown) if e.value == "j" + prompt.trigger(:keyright) if e.value == "l" + prompt.trigger(:keyleft) if e.value == "h" + } + prompt.input << "j" << "l" << "k" << "k" << "h" << " " << "\r" + prompt.input.rewind + + answer = prompt.multi_select("What number?", choices, default: 2, per_page: 4) + + expect(answer).to eq(['2', '1']) + + expected_output = [ + output_helper("What number?", choices[0..3], "2", ["2"], init: true, + hint: "Use #{up_down} and #{left_right} arrow keys, press Space to select and Enter to finish"), + output_helper("What number?", choices[0..3], "3", ["2"]), + output_helper("What number?", choices[4..7], "7", ["2"]), + output_helper("What number?", choices[4..7], "6", ["2"]), + output_helper("What number?", choices[3..6], "5", ["2"]), + output_helper("What number?", choices[0..3], "1", ["2"]), + output_helper("What number?", choices[0..3], "1", ["2", "1"]), + "What number? \e[32m2, 1\e[0m\n\e[?25h", + ].join + + expect(prompt.output.string).to eq(expected_output) + end end context "with :cycle" do it "doesn't cycle by default" do prompt = TTY::TestPrompt.new @@ -368,11 +503,11 @@ value = prompt.multi_select("What letter?", choices) expect(value).to eq(["C"]) expect(prompt.output.string).to eq( output_helper("What letter?", choices, "A", [], init: true, - hint: "Use arrow keys, press Space to select and Enter to finish") + + hint: "Use #{up_down} arrow keys, press Space to select and Enter to finish") + output_helper("What letter?", choices, "B", []) + output_helper("What letter?", choices, "C", []) + output_helper("What letter?", choices, "C", []) + output_helper("What letter?", choices, "C", ["C"]) + "What letter? \e[32mC\e[0m\n\e[?25h" @@ -383,22 +518,82 @@ prompt = TTY::TestPrompt.new choices = %w(A B C) prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" } prompt.input << "j" << "j" << "j" << " " << "\r" prompt.input.rewind + value = prompt.multi_select("What letter?", choices, cycle: true) + expect(value).to eq(["A"]) expect(prompt.output.string).to eq( output_helper("What letter?", choices, "A", [], init: true, - hint: "Use arrow keys, press Space to select and Enter to finish") + + hint: "Use #{up_down} arrow keys, press Space to select and Enter to finish") + output_helper("What letter?", choices, "B", []) + output_helper("What letter?", choices, "C", []) + output_helper("What letter?", choices, "A", []) + output_helper("What letter?", choices, "A", ["A"]) + "What letter? \e[32mA\e[0m\n\e[?25h" ) end + + it "cycles choices using left/right arrows" do + prompt = TTY::TestPrompt.new + choices = ('1'..'10').to_a + prompt.on(:keypress) { |e| + prompt.trigger(:keyright) if e.value == "l" + prompt.trigger(:keyleft) if e.value == "h" + } + prompt.input << "l" << "l" << "l" << "h" << " " << "\r" + prompt.input.rewind + + answer = prompt.multi_select("What number?", choices, default: 2, per_page: 4, cycle: true) + + expect(answer).to eq(['2', '10']) + + expected_output = [ + "\e[?25lWhat number? 2 ", + "\e[90m(Use #{up_down} and #{left_right} arrow keys, press Space to select and Enter to finish)\e[0m\n", + " #{symbols[:radio_off]} 1\n", + "#{symbols[:marker]} \e[32m#{symbols[:radio_on]}\e[0m 2\n", + " #{symbols[:radio_off]} 3\n", + " #{symbols[:radio_off]} 4", + "\e[2K\e[1G\e[1A" * 4, + "\e[2K\e[1G", + "What number? 2\n", + " #{symbols[:radio_off]} 5\n", + "#{symbols[:marker]} #{symbols[:radio_off]} 6\n", + " #{symbols[:radio_off]} 7\n", + " #{symbols[:radio_off]} 8", + "\e[2K\e[1G\e[1A" * 4, + "\e[2K\e[1G", + "What number? 2\n", + " #{symbols[:radio_off]} 9\n", + "#{symbols[:marker]} #{symbols[:radio_off]} 10", + "\e[2K\e[1G\e[1A" * 2, + "\e[2K\e[1G", + "What number? 2\n", + " #{symbols[:radio_off]} 1\n", + "#{symbols[:marker]} \e[32m#{symbols[:radio_on]}\e[0m 2\n", + " #{symbols[:radio_off]} 3\n", + " #{symbols[:radio_off]} 4", + "\e[2K\e[1G\e[1A" * 4, + "\e[2K\e[1G", + "What number? 2\n", + " #{symbols[:radio_off]} 9\n", + "#{symbols[:marker]} #{symbols[:radio_off]} 10", + "\e[2K\e[1G\e[1A" * 2, + "\e[2K\e[1G", + "What number? 2, 10\n", + " #{symbols[:radio_off]} 9\n", + "#{symbols[:marker]} \e[32m#{symbols[:radio_on]}\e[0m 10", + "\e[2K\e[1G\e[1A" * 2, + "\e[2K\e[1G", + "What number? \e[32m2, 10\e[0m\n\e[?25h", + ].join + + expect(prompt.output.string).to eq(expected_output) + end end context "with filter" do it "doesn't lose the selection when switching between filters" do prompt = TTY::TestPrompt.new @@ -411,11 +606,12 @@ answer = prompt.multi_select("What size?", %w(Tiny Medium Large Huge), filter: true) expect(answer).to eql(%w(Tiny Large)) expected_prompt_output = - output_helper("What size?", %w(Tiny Medium Large Huge), "Tiny", %w(), init: true, hint: "Use arrow keys, press Space to select and Enter to finish, and letter keys to filter") + + output_helper("What size?", %w(Tiny Medium Large Huge), "Tiny", %w(), init: true, + hint: "Use #{up_down} arrow keys, press Space to select and Enter to finish, and letter keys to filter") + output_helper("What size?", %w(Tiny Medium Large Huge), "Tiny", %w(Tiny)) + output_helper("What size?", %w(Large), "Large", %w(Tiny), hint: 'Filter: "a"') + output_helper("What size?", %w(Large), "Large", %w(Tiny Large), hint: 'Filter: "a"') + output_helper("What size?", %w(Tiny Medium Large Huge), "Tiny", %w(Tiny Large)) + exit_message("What size?", %w(Tiny Large)) @@ -465,11 +661,12 @@ answer = prompt.multi_select("What letter?", choices) expect(answer).to eq(%w[D E]) expected_output = - output_helper("What letter?", choices, "A", [], init: true, hint: "Use arrow keys, press Space to select and Enter to finish") + + output_helper("What letter?", choices, "A", [], init: true, + hint: "Use #{up_down} arrow keys, press Space to select and Enter to finish") + output_helper("What letter?", choices, "D", []) + output_helper("What letter?", choices, "D", %w[D]) + output_helper("What letter?", choices, "E", %w[D]) + output_helper("What letter?", choices, "E", %w[D E]) + exit_message("What letter?", %w[D E]) @@ -498,13 +695,46 @@ menu.choice :bourbon, 5 end expect(answer).to eq([1]) expected_output = - output_helper("Select drinks?", choices, "vodka", [], init: true, enum: ') ', hint: "Use arrow or number (1-5) keys, press Space to select and Enter to finish") + + output_helper("Select drinks?", choices, "vodka", [], init: true, enum: ') ', + hint: "Use #{up_down} arrow or number (1-5) keys, press Space to select and Enter to finish") + output_helper("Select drinks?", choices, "vodka", [], enum: ') ') + output_helper("Select drinks?", choices, "vodka", %w[vodka], enum: ') ') + exit_message("Select drinks?", %w[vodka]) + + expect(prompt.output.string).to eq(expected_output) + end + end + + context "with :max" do + it "limits number of choices" do + prompt = TTY::TestPrompt.new + choices = %w(A B C) + prompt.on(:keypress) { |e| + prompt.trigger(:keyup) if e.value == "k" + prompt.trigger(:keydown) if e.value == "j" + } + prompt.input << " " << "j" << " " << "j" << " " << "k" << " " << "j" << " " << "\r" + prompt.input.rewind + + value = prompt.multi_select("What letter?", choices, max: 2, per_page: 100) + expect(value).to eq(["A", "C"]) + + expected_output = + output_helper("What letter?", choices, "A", [], init: true, max: 2, + hint: "Use #{up_down} arrow keys, press Space to select and Enter to finish") + + output_helper("What letter?", choices, "A", %w[A], max: 2) + + output_helper("What letter?", choices, "B", %w[A], max: 2) + + output_helper("What letter?", choices, "B", %w[A B], max: 2) + + output_helper("What letter?", choices, "C", %w[A B], max: 2) + + output_helper("What letter?", choices, "C", %w[A B], max: 2) + + output_helper("What letter?", choices, "B", %w[A B], max: 2) + + output_helper("What letter?", choices, "B", %w[A], max: 2) + + output_helper("What letter?", choices, "C", %w[A], max: 2) + + output_helper("What letter?", choices, "C", %w[A C], max: 2) + + exit_message("What letter?", %w[A C]) expect(prompt.output.string).to eq(expected_output) end end end