diff --git a/completions/sdk.fish b/completions/sdk.fish index 17fcb7e..054c203 100644 --- a/completions/sdk.fish +++ b/completions/sdk.fish @@ -88,14 +88,14 @@ complete -c sdk -f -n '__fish_sdkman_no_command' \ -d 'Install new version' complete -c sdk -f -n '__fish_sdkman_using_command i install' \ -a "(__fish_sdkman_candidates)" +# TODO complete available versions --> issue #4 complete -c sdk -f -n '__fish_sdkman_specifying_candidate i install' \ - # TODO complete available versions --> #4 -a 'a.b.c' \ -d "version list unavailable" complete -c sdk -f -n '__fish_sdkman_specifying_candidate i install' \ -a 'x.y.z' \ - -d "Add your own; specify path!" - # Implicit: complete files as fourth parameter + -d "Specify path to install custom version." +# Implicit: complete files as fourth parameter complete -c sdk -f -n '__fish_sdkman_command_has_enough_parameters 3 i install' # block diff --git a/test/features/completions.feature b/test/features/completions.feature index 74ee349..d2a2710 100644 --- a/test/features/completions.feature +++ b/test/features/completions.feature @@ -14,166 +14,179 @@ Feature: Shell Completion Scenario Outline: Commands complete When the user enters "" into the prompt Then completion should propose "" + But completion should not propose Examples: - | cmd | completions | - | b | b, broadcast | - | c | c, current | - | d | d, default | - | f | flush | - | h | h, help | - | i | i, install | - | in | install | - | l | list, ls | - | o | offline | - | r | rm | - | s | selfupdate | - | u | u, ug, uninstall, update, upgrade, use | - | un | uninstall | - | up | update, upgrade | - | us | use | - | v | v, version | - # Currently uncovered; include to catch new upstream commands: - | a | | - | e | | - | g | | - | j | | - | k | | - | m | | - | n | | - | p | | - | q | | - | t | | - | w | | - | x | | - | y | | - | z | | + | cmd | completions | exclusions | + | b | b, broadcast | /^[^b]+$/ | + | c | c, current | /^[^c]+$/ | + | d | d, default | /^[^d]+$/ | + | f | flush | /^[^f]+$/ | + | h | h, help | /^[^h]+$/ | + | i | i, install | /^[^i]+$/ | + | in | install | | + | l | list, ls | /^[^l]+$/ | + | o | offline | /^[^o]+$/ | + | r | rm | /^[^r]+$/ | + | s | selfupdate | /^[^s]+$/ | + | u | u, ug, uninstall, update, upgrade, use | /^[^u]+$/ | + | un | uninstall | | + | up | update, upgrade | | + | us | use | | + | v | v, version | /^[^v]+$/ | + # Currently uncovered (except by fuzzy matches); + # include negatives to prevent accidents: + | a | | /^a/ | + | e | | /^e/ | + | g | | /^g/ | + | j | | /^j/ | + | k | | /^k/ | + | m | | /^m/ | + | n | | /^n/ | + | p | | /^p/ | + | q | | /^q/ | + | t | | /^t/ | + | w | | /^w/ | + | x | | /^x/ | + | y | | /^y/ | + | z | | /^z/ | Scenario Outline: Completion for 'install' When the user enters "install " into the prompt Then completion should propose "" + But completion should not propose Examples: - | cmd | completions | - | an | ant | - | xyz | | - | 1. | | - | gra | gradle, grails | - | grad | gradle | - | gradk | | -# | ant 1.10. | 1.10.0, 1.10.1 | # TODO: list installable versions --> issue #4 - | ant 1.10.2-mine /tm | /tmp/ | - | 'ant 1.10.2-mine /tmp ' | | + | cmd | completions | exclusions | + | an | ant | gradle | + | xyz | | /.*/ | + | 1. | | /.*/ | + | gra | gradle, grails | ant | + | grad | gradle | ant, grails | + | gradk | | /.*/ | +# | ant 1.10. | 1.10.0, 1.10.1 | | # TODO: list installable versions --> issue #4 + | ant 1.10.2-mine /tm | /tmp/ | /bin | + | 'ant 1.10.2-mine /tmp ' | | /.*/ | + # NB: Excluding wildcard pattern /.*/ expresses "do not offer any completions" Scenario Outline: Completion for 'uninstall' When the user enters "uninstall " into the prompt Then completion should propose "" + But completion should not propose Examples: - | cmd | completions | - | | ant, crash | - | a | ant | - | j | | - | 1. | | - | an | ant | # installed - | gr | | # none installed - | xyz | | # no such candidate - | 'an ' | | # no such candidate installed - | 'ant 1' | 1.10.1, 1.9.9 | - | 'ant 1.10.' | 1.10.1 | - | 'ant 2' | | - | 'ant 1.10.1 ' | | + | cmd | completions | exclusions | + | | ant, crash | gradle | + | a | ant | gradle | + | j | | /.*/ | + | 1. | | /.*/ | + | an | ant | gradle, crash | # some installed + | gr | | /.*/ | # none installed + | xyz | | /.*/ | # no such candidate + | 'an ' | | /.*/ | # no such candidate installed + | 'ant 1' | 1.10.1, 1.9.9 | /^\w+$/ | + | 'ant 1.10.' | 1.10.1 | 1.9.9 | + | 'ant 2' | | /.*/ | + | 'ant 1.10.1 ' | | /.*/ | # only one version at a time Scenario Outline: Completion for 'list' When the user enters "list " into the prompt Then completion should propose "" + But completion should not propose Examples: - | cmd | completions | - | an | ant | - | xyz | | - | 1. | | - | 'ant ' | | + | cmd | completions | exclusions | + | an | ant | crash | + | xyz | | /.*/ | + | 1. | | /.*/ | + | 'ant ' | | /.*/ | Scenario Outline: Completion for 'use' When the user enters "use " into the prompt Then completion should propose "" + But completion should not propose Examples: - | cmd | completions | - | | ant, crash | - | an | ant | - | j | | - | 1. | | - | 'ant ' | 1.10.1, 1.9.9 | - | 'ant 1.10.1 ' | | + | cmd | completions | exclusions | + | | ant, crash | gradle | + | an | ant | crash, gradle | + | j | | /.*/ | + | 1. | | /.*/ | + | 'ant ' | 1.10.1, 1.9.9 | /^\w+$/ | + | 'ant 1.10.1 ' | | /.*/ | Scenario Outline: Completion for 'default' When the user enters "default " into the prompt Then completion should propose "" + But completion should not propose Examples: - | cmd | completions | - | | ant, crash | - | an | ant | - | j | | - | 1. | | - | 'ant ' | 1.10.1, 1.9.9 | - | 'ant 1.10.1 ' | | + | cmd | completions | exclusions | + | | ant, crash | gradle | + | an | ant | crash, gradle | + | j | | /.*/ | + | 1. | | /.*/ | + | 'ant ' | 1.10.1, 1.9.9 | /^\w+$/ | + | 'ant 1.10.1 ' | | /.*/ | Scenario Outline: Completion for 'current' When the user enters "current " into the prompt Then completion should propose "" + But completion should not propose Examples: - | cmd | completions | - | an | ant | # --> installed version - | ja | java | # --> not installed - | xyz | | - | 1. | | - | 'ant ' | | + | cmd | completions | exclusions | + | an | ant | gradle | # --> installed version + | gr | gradle | ant | # --> not installed + | xyz | | /.*/ | + | 1. | | /.*/ | + | 'ant ' | | /.*/ | Scenario Outline: Completion for 'upgrade' When the user enters "upgrade " into the prompt Then completion should propose "" + But completion should not propose Examples: - | cmd | completions | - | | ant, crash | - | an | ant | - | j | | - | 1. | | - | 'ant ' | | + | cmd | completions | exclusions | + | | ant, crash | gradle | + | an | ant | crash, gradle | + | j | | /.*/ | + | 1. | | /.*/ | + | 'ant ' | | /^\w+$/ | Scenario Outline: Completion for 'offline' When the user enters "offline " into the prompt Then completion should propose "" + But completion should not propose Examples: - | cmd | completions | - | | disable, enable | - | e | enable | - | d | disable | - | a | | - | 'enable ' | | + | cmd | completions | exclusions | + | | disable, enable | /^(?!disable\|enable).*$/ | # NB: \| escaped to get it past Gherkin's parser + | en | enable | /^(?!enable).*$/ | + | di | disable | /^(?!disable).*$/ | + | an | | /.*/ | + | 'enable ' | | /.*/ | Scenario Outline: Completion for 'selfupdate' When the user enters "selfupdate " into the prompt Then completion should propose "" + But completion should not propose Examples: - | cmd | completions | - | | force | - | f | force | - | a | | - | 'force ' | | + | cmd | completions | exclusions | + | | force | /^(?!force).*$/ | + | f | force | /^(?!force).*$/ | + | a | | /.*/ | + | 'force ' | | /.*/ | Scenario Outline: Completion for 'flush' When the user enters "flush " into the prompt Then completion should propose "" + But completion should not propose Examples: - | cmd | completions | - | | archives, broadcast, temp | - | b | broadcast | - | a | archives | - | t | temp | - | x | | - | 'temp ' | | + | cmd | completions | exclusions | + | | archives, broadcast, temp | /^(?!archives\|broadcast\|temp).*$/ | + | b | broadcast | /^(?!broadcast).*$/ | + | a | archives | /^(?!archives\|broadcast).*$/ | + | t | temp | /^(?!temp\|broadcast).*$/ | + | x | | /.*/ | + | 'temp ' | | /.*/ | Scenario Outline: Completion for commands without parameters When the user enters "" into the prompt - Then completion should propose "" + Then completion should not propose /.*/ Examples: | cmd | | 'version ' | diff --git a/test/features/step_definitions/completion.rb b/test/features/step_definitions/completion.rb index 69f8cea..d7651dd 100644 --- a/test/features/step_definitions/completion.rb +++ b/test/features/step_definitions/completion.rb @@ -7,12 +7,8 @@ module CompletionHelper completions = run_fish_command("complete -C\"sdk #{cmd}\"") completions.split("\n") \ - .map { |line| line.split(/\s+/)[0].strip } \ - .sort \ - .uniq \ - .join(', ') + .map { |line| line.split(/\s+/)[0].strip } # TODO: Why do we get duplicates in the Docker container? - # --> remove uniq end end World CompletionHelper @@ -21,6 +17,15 @@ When('the user enters {string} into the prompt') do |cmd| @response = complete(cmd.gsub(/["']/, '')) end -Then(/^completion should propose "(.*)"$/) do |completions| - expect(@response).to eq(completions) +Then('completion should propose {string}') do |completions| + completions = completions.split(',').map(&:strip) + expect(@response).to include(*completions) +end + +Then('completion should not propose {patterns}') do |exclusions_patterns| + exclusions_patterns.each do |p| + @response.each do |r| + expect(r).not_to match(p) + end + end end diff --git a/test/features/support/parameter_types.rb b/test/features/support/parameter_types.rb new file mode 100644 index 0000000..a1f5f76 --- /dev/null +++ b/test/features/support/parameter_types.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +ParameterType( + name: 'patterns', + regexp: %r{([^\s]*|'[^']*'|/[^/]*/)(,\s*([^\s]*|'[^']*'|/[^/]*/))*}, + type: Array, + transformer: lambda do |*patterns| + patterns \ + .map(&:strip) \ + .map do |s| + s = if %r{^/(.*)/$} =~ s + Regexp.last_match(1) + elsif %r{^'(.*)'$} =~ s + "^#{Regexp.escape(Regexp.last_match(1))}$" + else + "^#{Regexp.escape(s)}$" + end + Regexp.compile(s) + end + end +)