diff options
author | Abinoam P. Marques Jr <abinoam@gmail.com> | 2016-02-19 04:30:15 -0300 |
---|---|---|
committer | Abinoam P. Marques Jr <abinoam@gmail.com> | 2016-02-19 04:30:15 -0300 |
commit | a3166e918311f3caf24fba89b76179c62c3ccbd7 (patch) | |
tree | 6aa7de97ce479a2d4fb2b11b920b0b9013760f0a | |
parent | 446ceb184dbfffbd83bd25edb15bcee3e630b3e4 (diff) | |
parent | d82032b2439241f7f05aa0294538d28cc4142b4d (diff) | |
download | highline-a3166e918311f3caf24fba89b76179c62c3ccbd7.tar.gz |
Merge pull request #186 from JEG2/pr184_rebasedv2.0.0.pre.develop.6
Rebased PR #184 (original by @matrinox)
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Changelog.md | 7 | ||||
-rwxr-xr-x | lib/highline.rb | 13 | ||||
-rw-r--r-- | lib/highline/menu.rb | 196 | ||||
-rwxr-xr-x | lib/highline/question.rb | 36 | ||||
-rw-r--r-- | lib/highline/question_asker.rb | 17 | ||||
-rw-r--r-- | lib/highline/version.rb | 2 | ||||
-rw-r--r-- | test/test_menu.rb | 303 |
8 files changed, 444 insertions, 131 deletions
@@ -2,3 +2,4 @@ doc pkg .DS_Store coverage +Gemfile.lock diff --git a/Changelog.md b/Changelog.md index fb95962..fe42734 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,13 @@ Below is a complete listing of changes for each revision of HighLine. +### 2.0.0-develop.6 / 2016-02-01 +* PR #184 - Menu improvements, bug fixes, and more tests by Geoff Lee (@matrinox) + * Add third arg to menu that overides the choice displayed to the user + * FIX: autocomplete prompt does not include menu choices after the first + * Add specs to cover the new features and the bug fix +* PR #183 - Fix menu example in README.md by Fabien Foerster (@fabienfoerster) + ### 2.0.0-develop.5 / 2015-12-27 * Fix #180 with PR #181 - Make it possible to overwrite the menu prompt shown on errors. diff --git a/lib/highline.rb b/lib/highline.rb index ba409b6..775a4ac 100755 --- a/lib/highline.rb +++ b/lib/highline.rb @@ -251,11 +251,18 @@ class HighLine menu.answer_type = menu.shell ? shell_style_lambda(menu) : menu.options selected = ask(menu) + return unless selected if menu.shell - menu.select(self, *selected) + selection, details = selected else - menu.select(self, selected) + selection = selected + end + + if menu.gather + menu.gather_selected(self, selection, details) + else + menu.select(self, selection, details) end end @@ -485,7 +492,7 @@ class HighLine # of the question. # def explain_error(error, question) - say(question.responses[error]) if error + say(question.final_responses[error]) if error say(question.ask_on_error_msg) end diff --git a/lib/highline/menu.rb b/lib/highline/menu.rb index e0c18de..1560fde 100644 --- a/lib/highline/menu.rb +++ b/lib/highline/menu.rb @@ -154,21 +154,46 @@ class HighLine # cli.choose do |menu| # menu.shell = true # - # menu.choice(:load, "Load a file.") - # menu.choice(:save, "Save data in file.") - # menu.choice(:quit, "Exit program.") + # menu.choice(:load, text: 'Load a file', help: "Load a file using your favourite editor.") + # menu.choice(:save, help: "Save data in file.") + # menu.choice(:quit, help: "Exit program.") # # menu.help("rules", "The rules of this system are as follows...") # end - def choice( name, help = nil, &action ) - @items << [name, action] - - @help[name.to_s.downcase] = help if help + def choice( name, help = nil, text = nil, &action ) + item = MenuItem.new(name, text: text, help: help, action: action) + @items << item + @help.merge!(item.item_help) update_responses # rebuild responses based on our settings end # + # This method helps reduce the namespaces in the original call, which would look + # like this: HighLine::Menu::MenuItem.new(...) + # With #build_item, it looks like this: menu.build_item(...) + # @param *args splat args, the same args you would pass to an initialization of + # HighLine::Menu::MenuItem + # @return [HighLine::Menu::MenuItem] the menu item + + def build_item(*args) + MenuItem.new(*args) + end + + # + # Adds an item directly to the menu. If you want more configuraiton or options, + # use this method + # + # @param item [Menu::MenuItem] item containing choice fields and more + # @return [void] + + def add_item(item) + @items << item + @help.merge!(item.item_help) + update_responses + end + + # # A shortcut for multiple calls to the sister method {#choice}. <b>Be # warned:</b> An _action_ set here will apply to *all* provided # _names_. This is considered to be a feature, so you can easily @@ -177,6 +202,10 @@ class HighLine # @param action (see #choice) # @return [void] # @example (see HighLine::Menu#initialize) + # + # choice has more options available to you, like longer text or help (and + # of course, individual actions) + # def choices( *names, &action ) names.each { |n| choice(n, &action) } end @@ -189,9 +218,9 @@ class HighLine # @return (see #choice) def hidden( name, help = nil, &action ) - @hidden_items << [name, action] - - @help[name.to_s.downcase] = help if help + item = MenuItem.new(name, text: name, help: help, action: action) + @hidden_items << item + @help.merge!(item.item_help) end # @@ -302,27 +331,29 @@ class HighLine # def options( ) # add in any hidden menu commands - @items.concat(@hidden_items) - - by_index = if @index == :letter - l_index = "`" - @items.map { "#{l_index.succ!}" } - else - (1 .. @items.size).collect { |s| String(s) } - end - by_name = @items.collect { |c| c.first } + items = all_items case @select_by when :index then - by_index + map_items_by_index(items, @index) when :name - by_name + items.map(&:name) + else + map_items_by_index(items, @index) + items.map(&:name) + end + end + + def map_items_by_index(items, index = nil) + if index == :letter + l_index = "`" + items.map { "#{l_index.succ!}" } else - by_index + by_name + (1 .. items.size).map(&:to_s) end - ensure - # make sure the hidden items are removed, before we return - @items.slice!(@items.size - @hidden_items.size, @hidden_items.size) + end + + def all_items + @items + @hidden_items end # @@ -337,44 +368,83 @@ class HighLine # else it returns the action return value. def select( highline_context, selection, details = nil ) # add in any hidden menu commands - @items.concat(@hidden_items) + items = all_items # Find the selected action. - name, action = if selection =~ /^\d+$/ # is a number? - get_item_by_number(selection) - else - get_item_by_letter(selection) - end + selected_item = find_item_from_selection(items, selection) # Run or return it. - if action - @highline = highline_context - if @shell - result = action.call(name, details) - else - result = action.call(name) - end - @nil_on_handled ? nil : result + @highline = highline_context + value_for_selected_item(selected_item, details) + end + + def find_item_from_selection(items, selection) + if selection =~ /^\d+$/ # is a number? + get_item_by_number(items, selection) else - name + get_item_by_letter(items, selection) end - ensure - # make sure the hidden items are removed, before we return - @items.slice!(@items.size - @hidden_items.size, @hidden_items.size) end # Returns the menu item referenced by its index # @param selection [Integer] menu item's index. - def get_item_by_number(selection) - @items[selection.to_i - 1] + def get_item_by_number(items, selection) + items[selection.to_i - 1] end # Returns the menu item referenced by its title/header/name. # @param selection [String] menu's title/header/name - def get_item_by_letter(selection) + def get_item_by_letter(items, selection) + item = items.find { |i| i.name == selection } + return item if item l_index = "`" # character before the letter "a" - index = @items.map { "#{l_index.succ!}" }.index(selection) - @items.find { |c| c.first == selection } or @items[index] + index = items.map { "#{l_index.succ!}" }.index(selection) + items[index] + end + + def value_for_selected_item(item, details) + if item.action + if @shell + result = item.action.call(item.name, details) + else + result = item.action.call(item.name) + end + @nil_on_handled ? nil : result + else + item.name + end + end + + def gather_selected(highline_context, selections, details = nil) + @highline = highline_context + # add in any hidden menu commands + items = all_items + + if selections.is_a?(Array) + value_for_array_selections(items, selections, details) + elsif selections.is_a?(Hash) + value_for_hash_selections(items, selections, details) + else + fail ArgumentError, 'selections must be either Array or Hash' + end + end + + def value_for_array_selections(items, selections, details) + # Find the selected items and return values + selected_items = selections.map do |selection| + find_item_from_selection(items, selection) + end + selected_items.map do |selected_item| + value_for_selected_item(selected_item, details) + end + end + + def value_for_hash_selections(items, selections, details) + # Find the selected items and return in hash form + selections.each_with_object({}) do |(key, selection), memo| + selected_item = find_item_from_selection(items, selection) + memo[key] = value_for_selected_item(selected_item, details) + end end # @@ -385,14 +455,14 @@ class HighLine def to_ary( ) case @index when :number - @items.map { |c| "#{@items.index(c) + 1}#{@index_suffix}#{c.first}" } + @items.map { |i| "#{@items.index(i) + 1}#{@index_suffix}#{i.text}" } when :letter l_index = "`" - @items.map { |c| "#{l_index.succ!}#{@index_suffix}#{c.first}" } + @items.map { |i| "#{l_index.succ!}#{@index_suffix}#{i.text}" } when :none - @items.map { |c| "#{c.first}" } + @items.map { |i| "#{i.text}" } else - @items.map { |c| "#{index}#{@index_suffix}#{c.first}" } + @items.map { |i| "#{index}#{@index_suffix}#{i.text}" } end end @@ -430,5 +500,27 @@ class HighLine def update_responses build_responses(options) end + + class MenuItem + attr_reader :name, :text, :help, :action + + # + # @param name [String] The name that is matched against the user input + # @param text: [String] The text that displays for that choice (defaults to name) + # @param help: [String] help, see above (not sure how it works) + # @param action: [Block] a block that gets called when choice is selected + # + def initialize(name, attributes) + @name = name + @text = attributes[:text] || @name + @help = attributes[:help] + @action = attributes[:action] + end + + def item_help + return {} unless help + { name.to_s.downcase => help } + end + end end end diff --git a/lib/highline/question.rb b/lib/highline/question.rb index 769da39..3724c67 100755 --- a/lib/highline/question.rb +++ b/lib/highline/question.rb @@ -61,7 +61,8 @@ class HighLine @first_answer = nil @directory = Pathname.new(File.expand_path(File.dirname($0))) @glob = "*" - @responses = Hash.new + @user_responses = Hash.new + @internal_responses = default_responses_hash @overwrite = false # allow block to override settings @@ -214,7 +215,9 @@ class HighLine # <tt>:not_valid</tt>:: The error message shown when # validation checks fail. # - attr_reader :responses + def responses + @user_responses + end # # When set to +true+ the question is asked, but output does not progress to # the next line. The Cursor is moved back to the beginning of the question @@ -242,16 +245,21 @@ class HighLine # @return [Hash] responses Hash winner (new and old merge). # @param message_source [Class] Array or String for example. # Same as {#answer_type}. - # @param new_hash_wins [Boolean] merge precedence (new vs. old). - def build_responses(message_source = answer_type, new_hash_wins = false) + def build_responses(message_source = answer_type) append_default if [::String, Symbol].include? default.class - old_hash = responses - new_hash = build_responses_new_hash(message_source) + # Update our internal responses with the new hash + # generated from the message source + @internal_responses = @internal_responses.merge(new_hash) + end - @responses = new_hash_wins ? old_hash.merge(new_hash) : new_hash.merge(old_hash) + def default_responses_hash + { + :ask_on_error => "? ", + :mismatch => "Your entries didn't match." + } end # When updating the responses hash, it generates the new one. @@ -260,17 +268,21 @@ class HighLine def build_responses_new_hash(message_source) { :ambiguous_completion => "Ambiguous choice. Please choose one of " + choice_error_str(message_source) + '.', - :ask_on_error => "? ", :invalid_type => "You must enter a valid #{message_source}.", :no_completion => "You must choose one of " + choice_error_str(message_source) + '.', :not_in_range => "Your answer isn't within the expected range " + "(#{expected_range}).", - :mismatch => "Your entries didn't match.", :not_valid => "Your answer isn't valid (must match " + "#{validate.inspect})." } end + # This is the actual responses hash that gets used in determining output + # Notice that we give @user_responses precedence over the responses + # generated internally via build_response + def final_responses + @internal_responses.merge(@user_responses) + end # # Returns the provided _answer_string_ after changing character case by @@ -531,10 +543,10 @@ class HighLine # @return [self] if :ask_on_error on responses Hash is set to :question # @return [String] if :ask_on_error on responses Hash is set to something else def ask_on_error_msg - if responses[:ask_on_error] == :question + if final_responses[:ask_on_error] == :question self - elsif responses[:ask_on_error] - responses[:ask_on_error] + elsif final_responses[:ask_on_error] + final_responses[:ask_on_error] end end diff --git a/lib/highline/question_asker.rb b/lib/highline/question_asker.rb index b8c8ebc..06e6647 100644 --- a/lib/highline/question_asker.rb +++ b/lib/highline/question_asker.rb @@ -105,13 +105,11 @@ class HighLine # with keys provided by the Hash on {Question#gather} # @return [Hash] def gather_hash - answers = {} - - question.gather.keys.sort.each do |key| + sorted_keys = question.gather.keys.sort_by(&:to_s) + sorted_keys.each_with_object({}) do |key, answers| @highline.key = key answers[key] = ask_once end - answers end @@ -119,7 +117,7 @@ class HighLine ## Delegate to Highline def explain_error(error) - @highline.say(question.responses[error]) if error + @highline.say(question.final_responses[error]) if error @highline.say(question.ask_on_error_msg) end @@ -133,15 +131,18 @@ class HighLine end def answer_matches_regex(answer) - (question.gather.is_a?(::String) && answer.to_s == question.gather) || - (question.gather.is_a?(Regexp) && answer.to_s =~ question.gather) + if question.gather.is_a?(::String) || question.gather.is_a?(Symbol) + answer.to_s == question.gather.to_s + else question.gather.is_a?(Regexp) + answer.to_s =~ question.gather + end end def gather_answers_based_on_type case question.gather when Integer gather_integer - when ::String, Regexp + when ::String, Symbol, Regexp gather_regexp when Hash gather_hash diff --git a/lib/highline/version.rb b/lib/highline/version.rb index 7977660..d4d5537 100644 --- a/lib/highline/version.rb +++ b/lib/highline/version.rb @@ -2,5 +2,5 @@ class HighLine # The version of the installed library. - VERSION = "2.0.0-develop.5".freeze + VERSION = "2.0.0-develop.6".freeze end diff --git a/test/test_menu.rb b/test/test_menu.rb index 594f622..f870245 100644 --- a/test/test_menu.rb +++ b/test/test_menu.rb @@ -37,21 +37,21 @@ class TestMenu < Minitest::Test @terminal.choose do |menu| # Default: menu.flow = :rows - - menu.choice "Sample1" - menu.choice "Sample2" - menu.choice "Sample3" + + menu.choice "Sample1" + menu.choice "Sample2" + menu.choice "Sample3" end assert_equal("1. Sample1\n2. Sample2\n3. Sample3\n? ", @output.string) @output.truncate(@output.rewind) @input.rewind - + @terminal.choose do |menu| menu.flow = :columns_across - - menu.choice "Sample1" - menu.choice "Sample2" + + menu.choice "Sample1" + menu.choice "Sample2" menu.choice "Sample3" end assert_equal("1. Sample1 2. Sample2 3. Sample3\n? ", @output.string) @@ -63,13 +63,13 @@ class TestMenu < Minitest::Test menu.flow = :inline menu.index = :none - menu.choice "Sample1" - menu.choice "Sample2" - menu.choice "Sample3" + menu.choice "Sample1" + menu.choice "Sample2" + menu.choice "Sample3" end assert_equal("Sample1, Sample2 or Sample3? ", @output.string) end - + def test_unicode_flow @input << "1\n" @input.rewind @@ -81,6 +81,76 @@ class TestMenu < Minitest::Test assert_equal("1. Unicode right single quotation mark: ’\n? ".encode(@output.external_encoding, { :undef => :replace }), @output.string) end + def test_text_override_index_selects_name + @input << "1\n" + @input.rewind + + selected = @terminal.choose do |menu| + menu.choice("Sample1", nil, "Sample2") + menu.choice("Sample2", nil, "Sample1") + end + assert_equal(selected, "Sample1") + assert_equal("1. Sample2\n" + + "2. Sample1\n" + + "? ", @output.string) + end + + def test_text_override_selections_matches_name + @input << "Sample2\n" + @input.rewind + + selected = @terminal.choose do |menu| + menu.choice("Sample1", nil, "Sample2") + menu.choice("Sample2", nil, "Sample1") + end + assert_equal(selected, "Sample2") + assert_equal("1. Sample2\n" + + "2. Sample1\n" + + "? ", @output.string) + end + + def test_menu_add_item_index_selects_name + @input << "1\n" + @input.rewind + + selected = @terminal.choose do |menu| + menu.add_item(HighLine::Menu::MenuItem.new("Sample1", text: "Sample2")) + menu.add_item(HighLine::Menu::MenuItem.new("Sample2", text: "Sample1")) + end + assert_equal(selected, "Sample1") + assert_equal("1. Sample2\n" + + "2. Sample1\n" + + "? ", @output.string) + end + + def test_menu_add_item_selections_matches_name + @input << "Sample2\n" + @input.rewind + + selected = @terminal.choose do |menu| + menu.add_item(HighLine::Menu::MenuItem.new("Sample1", text: "Sample2")) + menu.add_item(HighLine::Menu::MenuItem.new("Sample2", text: "Sample1")) + end + assert_equal(selected, "Sample2") + assert_equal("1. Sample2\n" + + "2. Sample1\n" + + "? ", @output.string) + end + + def test_menu_build_item + @input << "Sample2\n" + @input.rewind + + selected = @terminal.choose do |menu| + menu.add_item(menu.build_item("Sample1", text: "Sample2")) + menu.add_item(menu.build_item("Sample2", text: "Sample1")) + end + assert_equal(selected, "Sample2") + assert_equal("1. Sample2\n" + + "2. Sample1\n" + + "? ", @output.string) + end + def test_help @input << "help\nhelp load\nhelp rules\nhelp missing\n" @input.rewind @@ -92,7 +162,7 @@ class TestMenu < Minitest::Test menu.choice(:load, "Load a file.") menu.choice(:save, "Save data in file.") menu.choice(:quit, "Exit program.") - + menu.help("rules", "The rules of this system are as follows...") end end @@ -102,15 +172,15 @@ class TestMenu < Minitest::Test "specific topic enter:\n" + "\thelp [TOPIC]\n" + "Try asking for help on any of the following:\n" + - "\nload quit rules save \n" + + "\nload quit rules save \n" + "1. load\n2. save\n3. quit\n4. help\n? " + - "= load\n\n" + + "= load\n\n" + "Load a file.\n" + "1. load\n2. save\n3. quit\n4. help\n? " + "= rules\n\n" + "The rules of this system are as follows...\n" + "1. load\n2. save\n3. quit\n4. help\n? " + - "= missing\n\n" + + "= missing\n\n" + "There's no help for that topic.\n", @output.string ) end @@ -120,22 +190,22 @@ class TestMenu < Minitest::Test @terminal.choose do |menu| # Default: menu.index = :number - - menu.choice "Sample1" - menu.choice "Sample2" - menu.choice "Sample3" + + menu.choice "Sample1" + menu.choice "Sample2" + menu.choice "Sample3" end assert_equal("1. Sample1\n2. Sample2\n3. Sample3\n? ", @output.string) @output.truncate(@output.rewind) @input.rewind - + @terminal.choose do |menu| menu.index = :letter menu.index_suffix = ") " - - menu.choice "Sample1" - menu.choice "Sample2" + + menu.choice "Sample1" + menu.choice "Sample2" menu.choice "Sample3" end assert_equal("a) Sample1\nb) Sample2\nc) Sample3\n? ", @output.string) @@ -146,15 +216,15 @@ class TestMenu < Minitest::Test @terminal.choose do |menu| menu.index = :none - menu.choice "Sample1" - menu.choice "Sample2" - menu.choice "Sample3" + menu.choice "Sample1" + menu.choice "Sample2" + menu.choice "Sample3" end assert_equal("Sample1\nSample2\nSample3\n? ", @output.string) @output.truncate(@output.rewind) @input.rewind - + @terminal.choose do |menu| menu.index = "*" @@ -164,11 +234,11 @@ class TestMenu < Minitest::Test end assert_equal("* Sample1\n* Sample2\n* Sample3\n? ", @output.string) end - + def test_layouts @input << "save\n" @input.rewind - + @terminal.choose(:load, :save, :quit) # Default: layout = :list assert_equal("1. load\n2. save\n3. quit\n? ", @output.string) @@ -178,7 +248,7 @@ class TestMenu < Minitest::Test @terminal.choose(:load, :save, :quit) do |menu| menu.header = "File Menu" end - assert_equal( "File Menu:\n" + + assert_equal( "File Menu:\n" + "1. load\n2. save\n3. quit\n? ", @output.string ) @input.rewind @@ -189,7 +259,7 @@ class TestMenu < Minitest::Test menu.header = "File Menu" menu.prompt = "Operation? " end - assert_equal( "File Menu: Operation? " + + assert_equal( "File Menu: Operation? " + "(load, save or quit) ", @output.string ) @input.rewind @@ -208,7 +278,7 @@ class TestMenu < Minitest::Test end assert_equal("1. load\n2. save\n3. quit\nFile Menu: ", @output.string) end - + def test_list_option @input << "l\n" @input.rewind @@ -252,18 +322,18 @@ class TestMenu < Minitest::Test end assert_equal("Sample2", output) end - + def test_passed_command @input << "q\n" @input.rewind - + selected = nil @terminal.choose do |menu| menu.choices(:load, :save, :quit) { |command| selected = command } end assert_equal(:quit, selected) end - + def test_question_options @input << "save\n" @input.rewind @@ -286,19 +356,19 @@ class TestMenu < Minitest::Test def test_select_by @input << "Sample1\n2\n" @input.rewind - + selected = @terminal.choose do |menu| menu.choice "Sample1" menu.choice "Sample2" menu.choice "Sample3" end assert_equal("Sample1", selected) - + @input.rewind selected = @terminal.choose do |menu| menu.select_by = :index - + menu.choice "Sample1" menu.choice "Sample2" menu.choice "Sample3" @@ -309,7 +379,7 @@ class TestMenu < Minitest::Test selected = @terminal.choose do |menu| menu.select_by = :name - + menu.choice "Sample1" menu.choice "Sample2" menu.choice "Sample3" @@ -320,7 +390,7 @@ class TestMenu < Minitest::Test def test_hidden @input << "Hidden\n4\n" @input.rewind - + selected = @terminal.choose do |menu| menu.choice "Sample1" menu.choice "Sample2" @@ -329,12 +399,12 @@ class TestMenu < Minitest::Test end assert_equal("Hidden!", selected) assert_equal("1. Sample1\n2. Sample2\n3. Sample3\n? ", @output.string) - + @input.rewind selected = @terminal.choose do |menu| menu.select_by = :index - + menu.choice "Sample1" menu.choice "Sample2" menu.choice "Sample3" @@ -346,7 +416,7 @@ class TestMenu < Minitest::Test selected = @terminal.choose do |menu| menu.select_by = :name - + menu.choice "Sample1" menu.choice "Sample2" menu.choice "Sample3" @@ -360,8 +430,8 @@ class TestMenu < Minitest::Test def test_select_by_letter @input << "b\n" @input.rewind - - selected = @terminal.choose do |menu| + + selected = @terminal.choose do |menu| menu.index = :letter menu.choice :save menu.choice :load @@ -369,7 +439,7 @@ class TestMenu < Minitest::Test end assert_equal(:load, selected) end - + def test_shell @input << "save --some-option my_file.txt\n" @input.rewind @@ -381,7 +451,7 @@ class TestMenu < Minitest::Test menu.choice(:save) do |command, details| selected = command options = details - + "Saved!" end menu.shell = true @@ -402,9 +472,9 @@ class TestMenu < Minitest::Test def test_symbols @input << "3\n" @input.rewind - + selected = @terminal.choose do |menu| - menu.choices(:save, :load, :quit) + menu.choices(:save, :load, :quit) end assert_equal(:quit, selected) end @@ -414,13 +484,12 @@ class TestMenu < Minitest::Test # Will page twice, so start with two new lines @input << "\n\n3\n" @input.rewind - - # Sadly this goes into an infinite loop without the fix to page_print - selected = @terminal.choose(* 1..10) + + # Sadly this goes into an infinite loop without the fix to page_print + selected = @terminal.choose(* 1..10) assert_equal(selected, 3) end - def test_cancel_paging # Tests that paging can be cancelled halfway through @terminal.page_at = 5 @@ -434,12 +503,31 @@ class TestMenu < Minitest::Test # Make sure paging message appeared assert( @output.string.index('press enter/return to continue or q to stop'), "Paging message did not appear." ) - + # Make sure it only appeared once assert( @output.string !~ /q to stop.*q to stop/m, "Paging message appeared more than once." ) end + def test_autocomplete_prompt + @input << "lisp\nRuby\n" + @input.rewind + + answer = @terminal.choose do |menu| + menu.choice(:Perl) + menu.choice(:Python) + menu.choice(:Ruby) + menu.prompt = "What is your favorite programming language? " + end + languages = [:Perl, :Python, :Ruby] + assert_equal("1. Perl\n" + + "2. Python\n" + + "3. Ruby\n" + + "What is your favorite programming language? " + + "You must choose one of [1, 2, 3, Perl, Python, Ruby].\n" + + "? ", @output.string ) + end + # Issue #180 - https://github.com/JEG2/highline/issues/180 def test_menu_prompt @input << "2\n1\n" @@ -458,4 +546,109 @@ class TestMenu < Minitest::Test assert_equal complete_interaction, @output.string end + + def test_menu_gather_integer + @input << "Sample1\nlast\n" + @input.rewind + + selected = @terminal.choose do |menu| + menu.gather = 2 + menu.choice "Sample1" + menu.choice "Sample2" + menu.choice "last" + end + assert_equal(["Sample1", "last"], selected) + + assert_equal("1. Sample1\n" + + "2. Sample2\n" + + "3. last\n" + + "? 1. Sample1\n" + + "2. Sample2\n" + + "3. last\n" + + "? ", @output.string) + end + + def test_menu_gather_string + @input << "Sample1\nlast\n" + @input.rewind + + selected = @terminal.choose do |menu| + menu.gather = :last + menu.choice "Sample1" + menu.choice "Sample2" + menu.choice :last + end + assert_equal(["Sample1"], selected) + + assert_equal("1. Sample1\n" + + "2. Sample2\n" + + "3. last\n" + + "? 1. Sample1\n" + + "2. Sample2\n" + + "3. last\n" + + "? ", @output.string) + end + + def test_menu_gather_symbol + @input << "Sample1\nlast\n" + @input.rewind + + selected = @terminal.choose do |menu| + menu.gather = "last" + menu.choice "Sample1" + menu.choice "Sample2" + menu.choice "last" + end + assert_equal(["Sample1"], selected) + + assert_equal("1. Sample1\n" + + "2. Sample2\n" + + "3. last\n" + + "? 1. Sample1\n" + + "2. Sample2\n" + + "3. last\n" + + "? ", @output.string) + end + + def test_menu_gather_regexp + @input << "Sample1\nlast\n" + @input.rewind + + selected = @terminal.choose do |menu| + menu.gather = /la/ + menu.choice "Sample1" + menu.choice "Sample2" + menu.choice "last" + end + assert_equal(["Sample1"], selected) + + assert_equal("1. Sample1\n" + + "2. Sample2\n" + + "3. last\n" + + "? 1. Sample1\n" + + "2. Sample2\n" + + "3. last\n" + + "? ", @output.string) + end + + def test_menu_gather_hash + @input << "Sample1\n3\n" + @input.rewind + + selected = @terminal.choose do |menu| + menu.gather = { "First" => true, second: true } + menu.choice "Sample1" + menu.choice "Sample2" + menu.choice "last" + end + assert_equal({ "First" => "Sample1", second: "last" }, selected) + + assert_equal("1. Sample1\n" + + "2. Sample2\n" + + "3. last\n" + + "? 1. Sample1\n" + + "2. Sample2\n" + + "3. last\n" + + "? ", @output.string) + end end |