diff options
| author | Junegunn Choi <junegunn.c@gmail.com> | 2025-01-25 19:57:40 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-01-25 19:57:40 +0900 |
| commit | 0237bf09bf1fe70d94727b040ac39110f56b497f (patch) | |
| tree | d32061f165f8bd0ba39ef3123623e16be2afe8a1 /test/test_shell_integration.rb | |
| parent | 04017c25bb719c31bb18d43b4bcb96c4736f8d91 (diff) | |
| download | fzf-0237bf09bf1fe70d94727b040ac39110f56b497f.tar.gz | |
Split integration test file (#4205)
Diffstat (limited to 'test/test_shell_integration.rb')
| -rw-r--r-- | test/test_shell_integration.rb | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/test/test_shell_integration.rb b/test/test_shell_integration.rb new file mode 100644 index 00000000..c3966479 --- /dev/null +++ b/test/test_shell_integration.rb @@ -0,0 +1,485 @@ +# frozen_string_literal: true + +require_relative 'lib/common' + +# Testing shell integration +module TestShell + attr_reader :tmux + + def setup + @tmux = Tmux.new(shell) + tmux.prepare + end + + def teardown + @tmux.kill + end + + def set_var(name, val) + tmux.prepare + tmux.send_keys "export #{name}='#{val}'", :Enter + tmux.prepare + end + + def unset_var(name) + tmux.prepare + tmux.send_keys "unset #{name}", :Enter + tmux.prepare + end + + def test_ctrl_t + set_var('FZF_CTRL_T_COMMAND', 'seq 100') + + tmux.prepare + tmux.send_keys 'C-t' + tmux.until { |lines| assert_equal 100, lines.item_count } + tmux.send_keys :Tab, :Tab, :Tab + tmux.until { |lines| assert lines.any_include?(' (3)') } + tmux.send_keys :Enter + tmux.until { |lines| assert lines.any_include?('1 2 3') } + tmux.send_keys 'C-c' + end + + def test_ctrl_t_unicode + writelines(['fzf-unicode 테스트1', 'fzf-unicode 테스트2']) + set_var('FZF_CTRL_T_COMMAND', "cat #{tempname}") + + tmux.prepare + tmux.send_keys 'echo ', 'C-t' + tmux.until { |lines| assert_equal 2, lines.item_count } + tmux.send_keys 'fzf-unicode' + tmux.until { |lines| assert_equal 2, lines.match_count } + + tmux.send_keys '1' + tmux.until { |lines| assert_equal 1, lines.match_count } + tmux.send_keys :Tab + tmux.until { |lines| assert_equal 1, lines.select_count } + + tmux.send_keys :BSpace + tmux.until { |lines| assert_equal 2, lines.match_count } + + tmux.send_keys '2' + tmux.until { |lines| assert_equal 1, lines.match_count } + tmux.send_keys :Tab + tmux.until { |lines| assert_equal 2, lines.select_count } + + tmux.send_keys :Enter + tmux.until { |lines| assert_match(/echo .*fzf-unicode.*1.* .*fzf-unicode.*2/, lines.join) } + tmux.send_keys :Enter + tmux.until { |lines| assert_equal 'fzf-unicode 테스트1 fzf-unicode 테스트2', lines[-1] } + end + + def test_alt_c + tmux.prepare + tmux.send_keys :Escape, :c + lines = tmux.until { |lines| assert_operator lines.match_count, :>, 0 } + expected = lines.reverse.find { |l| l.start_with?('> ') }[2..] + tmux.send_keys :Enter + tmux.prepare + tmux.send_keys :pwd, :Enter + tmux.until { |lines| assert lines[-1]&.end_with?(expected) } + end + + def test_alt_c_command + set_var('FZF_ALT_C_COMMAND', 'echo /tmp') + + tmux.prepare + tmux.send_keys 'cd /', :Enter + + tmux.prepare + tmux.send_keys :Escape, :c + tmux.until { |lines| assert_equal 1, lines.item_count } + tmux.send_keys :Enter + + tmux.prepare + tmux.send_keys :pwd, :Enter + tmux.until { |lines| assert_equal '/tmp', lines[-1] } + end + + def test_ctrl_r + tmux.prepare + tmux.send_keys 'echo 1st', :Enter + tmux.prepare + tmux.send_keys 'echo 2nd', :Enter + tmux.prepare + tmux.send_keys 'echo 3d', :Enter + tmux.prepare + 3.times do + tmux.send_keys 'echo 3rd', :Enter + tmux.prepare + end + tmux.send_keys 'echo 4th', :Enter + tmux.prepare + tmux.send_keys 'C-r' + tmux.until { |lines| assert_operator lines.match_count, :>, 0 } + tmux.send_keys 'e3d' + # Duplicates removed: 3d (1) + 3rd (1) => 2 matches + tmux.until { |lines| assert_equal 2, lines.match_count } + tmux.until { |lines| assert lines[-3]&.end_with?(' echo 3d') } + tmux.send_keys 'C-r' + tmux.until { |lines| assert lines[-3]&.end_with?(' echo 3rd') } + tmux.send_keys :Enter + tmux.until { |lines| assert_equal 'echo 3rd', lines[-1] } + tmux.send_keys :Enter + tmux.until { |lines| assert_equal '3rd', lines[-1] } + end + + def test_ctrl_r_multiline + # NOTE: Current bash implementation shows an extra new line if there's + # only entry in the history + tmux.send_keys ':', :Enter + tmux.send_keys 'echo "foo', :Enter, 'bar"', :Enter + tmux.until { |lines| assert_equal %w[foo bar], lines[-2..] } + tmux.prepare + tmux.send_keys 'C-r' + tmux.until { |lines| assert_equal '>', lines[-1] } + tmux.send_keys 'foo bar' + tmux.until { |lines| assert_includes lines[-4], '"foo' } unless shell == :zsh + tmux.until { |lines| assert lines[-3]&.match?(/bar"␊?/) } + tmux.send_keys :Enter + tmux.until { |lines| assert lines[-1]&.match?(/bar"␊?/) } + tmux.send_keys :Enter + tmux.until { |lines| assert_equal %w[foo bar], lines[-2..] } + end + + def test_ctrl_r_abort + skip("doesn't restore the original line when search is aborted pre Bash 4") if shell == :bash && `#{Shell.bash} --version`[/(?<= version )\d+/].to_i < 4 + %w[foo ' "].each do |query| + tmux.prepare + tmux.send_keys :Enter, query + tmux.until { |lines| assert lines[-1]&.start_with?(query) } + tmux.send_keys 'C-r' + tmux.until { |lines| assert_equal "> #{query}", lines[-1] } + tmux.send_keys 'C-g' + tmux.until { |lines| assert lines[-1]&.start_with?(query) } + end + end +end + +module CompletionTest + def test_file_completion + FileUtils.mkdir_p('/tmp/fzf-test') + FileUtils.mkdir_p('/tmp/fzf test') + (1..100).each { |i| FileUtils.touch("/tmp/fzf-test/#{i}") } + ['no~such~user', '/tmp/fzf test/foobar'].each do |f| + FileUtils.touch(File.expand_path(f)) + end + tmux.prepare + tmux.send_keys 'cat /tmp/fzf-test/10**', :Tab + tmux.until { |lines| assert_operator lines.match_count, :>, 0 } + tmux.send_keys ' !d' + tmux.until { |lines| assert_equal 2, lines.match_count } + tmux.send_keys :Tab, :Tab + tmux.until { |lines| assert_equal 2, lines.select_count } + tmux.send_keys :Enter + tmux.until(true) do |lines| + assert_equal 'cat /tmp/fzf-test/10 /tmp/fzf-test/100', lines[-1] + end + + # ~USERNAME**<TAB> + user = `whoami`.chomp + tmux.send_keys 'C-u' + tmux.send_keys "cat ~#{user}**", :Tab + tmux.until { |lines| assert_operator lines.match_count, :>, 0 } + tmux.send_keys "/#{user}" + tmux.until { |lines| assert(lines.any? { |l| l.end_with?("/#{user}") }) } + tmux.send_keys :Enter + tmux.until(true) do |lines| + assert_match %r{cat .*/#{user}}, lines[-1] + end + + # ~INVALID_USERNAME**<TAB> + tmux.send_keys 'C-u' + tmux.send_keys 'cat ~such**', :Tab + tmux.until(true) { |lines| assert lines.any_include?('no~such~user') } + tmux.send_keys :Enter + tmux.until(true) { |lines| assert_equal 'cat no~such~user', lines[-1] } + + # /tmp/fzf\ test**<TAB> + tmux.send_keys 'C-u' + tmux.send_keys 'cat /tmp/fzf\ test/**', :Tab + tmux.until { |lines| assert_operator lines.match_count, :>, 0 } + tmux.send_keys 'foobar$' + tmux.until do |lines| + assert_equal 1, lines.match_count + assert lines.any_include?('> /tmp/fzf test/foobar') + end + tmux.send_keys :Enter + tmux.until(true) { |lines| assert_equal 'cat /tmp/fzf\ test/foobar', lines[-1] } + + # Should include hidden files + (1..100).each { |i| FileUtils.touch("/tmp/fzf-test/.hidden-#{i}") } + tmux.send_keys 'C-u' + tmux.send_keys 'cat /tmp/fzf-test/hidden**', :Tab + tmux.until(true) do |lines| + assert_equal 100, lines.match_count + assert lines.any_include?('/tmp/fzf-test/.hidden-') + end + tmux.send_keys :Enter + ensure + ['/tmp/fzf-test', '/tmp/fzf test', '~/.fzf-home', 'no~such~user'].each do |f| + FileUtils.rm_rf(File.expand_path(f)) + end + end + + def test_file_completion_root + tmux.send_keys 'ls /**', :Tab + tmux.until { |lines| assert_operator lines.match_count, :>, 0 } + tmux.send_keys :Enter + end + + def test_dir_completion + (1..100).each do |idx| + FileUtils.mkdir_p("/tmp/fzf-test/d#{idx}") + end + FileUtils.touch('/tmp/fzf-test/d55/xxx') + tmux.prepare + tmux.send_keys 'cd /tmp/fzf-test/**', :Tab + tmux.until { |lines| assert_operator lines.match_count, :>, 0 } + tmux.send_keys :Tab, :Tab # Tab does not work here + tmux.send_keys 55 + tmux.until do |lines| + assert_equal 1, lines.match_count + assert_includes lines, '> 55' + assert_includes lines, '> /tmp/fzf-test/d55' + end + tmux.send_keys :Enter + tmux.until(true) { |lines| assert_equal 'cd /tmp/fzf-test/d55/', lines[-1] } + tmux.send_keys :xx + tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] } + + # Should not match regular files (bash-only) + if instance_of?(TestBash) + tmux.send_keys :Tab + tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] } + end + + # Fail back to plusdirs + tmux.send_keys :BSpace, :BSpace, :BSpace + tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55', lines[-1] } + tmux.send_keys :Tab + tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/', lines[-1] } + end + + def test_process_completion + tmux.send_keys 'sleep 12345 &', :Enter + lines = tmux.until { |lines| assert lines[-1]&.start_with?('[1] ') } + pid = lines[-1]&.split&.last + tmux.prepare + tmux.send_keys 'C-L' + tmux.send_keys 'kill **', :Tab + tmux.until { |lines| assert_operator lines.match_count, :>, 0 } + tmux.send_keys 'sleep12345' + tmux.until { |lines| assert lines.any_include?('sleep 12345') } + tmux.send_keys :Enter + tmux.until(true) { |lines| assert_equal "kill #{pid}", lines[-1] } + ensure + if pid + begin + Process.kill('KILL', pid.to_i) + rescue StandardError + nil + end + end + end + + def test_custom_completion + tmux.send_keys '_fzf_compgen_path() { echo "$1"; seq 10; }', :Enter + tmux.prepare + tmux.send_keys 'ls /tmp/**', :Tab + tmux.until { |lines| assert_equal 11, lines.match_count } + tmux.send_keys :Tab, :Tab, :Tab + tmux.until { |lines| assert_equal 3, lines.select_count } + tmux.send_keys :Enter + tmux.until(true) { |lines| assert_equal 'ls /tmp 1 2', lines[-1] } + end + + def test_unset_completion + tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter + tmux.prepare + + # Using tmux + tmux.send_keys 'unset FZFFOOBR**', :Tab + tmux.until { |lines| assert_equal 1, lines.match_count } + tmux.send_keys :Enter + tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] } + tmux.send_keys 'C-c' + + # FZF_TMUX=1 + new_shell + tmux.focus + tmux.send_keys 'unset FZFFOOBR**', :Tab + tmux.until { |lines| assert_equal 1, lines.match_count } + tmux.send_keys :Enter + tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] } + end + + def test_completion_in_command_sequence + tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter + tmux.prepare + + triggers = ['**', '~~', '++', 'ff', '/'] + triggers.push('&', '[', ';', '`') if instance_of?(TestZsh) + + triggers.each do |trigger| + set_var('FZF_COMPLETION_TRIGGER', trigger) + command = "echo foo; QUX=THUD unset FZFFOOBR#{trigger}" + tmux.send_keys command.sub(/(;|`)$/, '\\\\\1'), :Tab + tmux.until { |lines| assert_equal 1, lines.match_count } + tmux.send_keys :Enter + tmux.until { |lines| assert_equal 'echo foo; QUX=THUD unset FZFFOOBAR', lines[-1] } + end + end + + def test_file_completion_unicode + FileUtils.mkdir_p('/tmp/fzf-test') + tmux.paste "cd /tmp/fzf-test; echo test3 > $'fzf-unicode \\355\\205\\214\\354\\212\\244\\355\\212\\2701'; echo test4 > $'fzf-unicode \\355\\205\\214\\354\\212\\244\\355\\212\\2702'" + tmux.prepare + tmux.send_keys 'cat fzf-unicode**', :Tab + tmux.until { |lines| assert_equal 2, lines.match_count } + + tmux.send_keys '1' + tmux.until { |lines| assert_equal 1, lines.match_count } + tmux.send_keys :Tab + tmux.until { |lines| assert_equal 1, lines.select_count } + + tmux.send_keys :BSpace + tmux.until { |lines| assert_equal 2, lines.match_count } + + tmux.send_keys '2' + tmux.until { |lines| assert_equal 1, lines.select_count } + tmux.send_keys :Tab + tmux.until { |lines| assert_equal 2, lines.select_count } + + tmux.send_keys :Enter + tmux.until(true) { |lines| assert_match(/cat .*fzf-unicode.*1.* .*fzf-unicode.*2/, lines[-1]) } + tmux.send_keys :Enter + tmux.until { |lines| assert_equal %w[test3 test4], lines[-2..] } + end + + def test_custom_completion_api + tmux.send_keys 'eval "_fzf$(declare -f _comprun)"', :Enter + %w[f g].each do |command| + tmux.prepare + tmux.send_keys "#{command} b**", :Tab + tmux.until do |lines| + assert_equal 2, lines.item_count + assert_equal 1, lines.match_count + assert lines.any_include?("prompt-#{command}") + assert lines.any_include?("preview-#{command}-bar") + end + tmux.send_keys :Enter + tmux.until { |lines| assert_equal "#{command} #{command}barbar", lines[-1] } + tmux.send_keys 'C-u' + end + ensure + tmux.prepare + tmux.send_keys 'unset -f _fzf_comprun', :Enter + end + + def test_ssh_completion + (1..5).each { |i| FileUtils.touch("/tmp/fzf-test-ssh-#{i}") } + + tmux.send_keys 'ssh jg@localhost**', :Tab + tmux.until do |lines| + assert_operator lines.match_count, :>=, 1 + end + + tmux.send_keys :Enter + tmux.until { |lines| assert lines.any_include?('ssh jg@localhost') } + tmux.send_keys ' -i /tmp/fzf-test-ssh**', :Tab + tmux.until do |lines| + assert_operator lines.match_count, :>=, 5 + assert_equal 0, lines.select_count + end + tmux.send_keys :Tab, :Tab, :Tab + tmux.until do |lines| + assert_equal 3, lines.select_count + end + tmux.send_keys :Enter + tmux.until { |lines| assert lines.any_include?('ssh jg@localhost -i /tmp/fzf-test-ssh-') } + + tmux.send_keys 'localhost**', :Tab + tmux.until do |lines| + assert_operator lines.match_count, :>=, 1 + end + end +end + +class TestBash < TestBase + include TestShell + include CompletionTest + + def shell + :bash + end + + def new_shell + tmux.prepare + tmux.send_keys "FZF_TMUX=1 #{Shell.bash}", :Enter + tmux.prepare + end + + def test_dynamic_completion_loader + tmux.paste 'touch /tmp/foo; _fzf_completion_loader=1' + tmux.paste '_completion_loader() { complete -o default fake; }' + tmux.paste 'complete -F _fzf_path_completion -o default -o bashdefault fake' + tmux.send_keys 'fake /tmp/foo**', :Tab + tmux.until { |lines| assert_operator lines.match_count, :>, 0 } + tmux.send_keys 'C-c' + + tmux.prepare + tmux.send_keys 'fake /tmp/foo' + tmux.send_keys :Tab, 'C-u' + + tmux.prepare + tmux.send_keys 'fake /tmp/foo**', :Tab + tmux.until { |lines| assert_operator lines.match_count, :>, 0 } + end +end + +class TestZsh < TestBase + include TestShell + include CompletionTest + + def shell + :zsh + end + + def new_shell + tmux.send_keys "FZF_TMUX=1 #{Shell.zsh}", :Enter + tmux.prepare + end + + def test_complete_quoted_command + tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter + ['unset', '\unset', "'unset'"].each do |command| + tmux.prepare + tmux.send_keys "#{command} FZFFOOBR**", :Tab + tmux.until { |lines| assert_equal 1, lines.match_count } + tmux.send_keys :Enter + tmux.until { |lines| assert_equal "#{command} FZFFOOBAR", lines[-1] } + tmux.send_keys 'C-c' + end + end +end + +class TestFish < TestBase + include TestShell + + def shell + :fish + end + + def new_shell + tmux.send_keys 'env FZF_TMUX=1 FZF_DEFAULT_OPTS=--no-scrollbar fish', :Enter + tmux.send_keys 'function fish_prompt; end; clear', :Enter + tmux.until { |lines| assert_empty lines } + end + + def set_var(name, val) + tmux.prepare + tmux.send_keys "set -g #{name} '#{val}'", :Enter + tmux.prepare + end +end |
