diff options
Diffstat (limited to 'test')
100 files changed, 2527 insertions, 151 deletions
diff --git a/test/command_callback/inko_paths/test.inko b/test/command_callback/inko_paths/test.inko new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/inko_paths/test.inko diff --git a/test/command_callback/inko_paths/tests/test/test_foo.inko b/test/command_callback/inko_paths/tests/test/test_foo.inko new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/inko_paths/tests/test/test_foo.inko diff --git a/test/command_callback/php-intelephense-project/with-composer/composer.json b/test/command_callback/php-intelephense-project/with-composer/composer.json new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/php-intelephense-project/with-composer/composer.json diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe new file mode 100755 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe diff --git a/test/command_callback/python_paths/with_virtualenv/env/bin/autoimport b/test/command_callback/python_paths/with_virtualenv/env/bin/autoimport new file mode 100755 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/python_paths/with_virtualenv/env/bin/autoimport diff --git a/test/command_callback/python_paths/with_virtualenv/env/bin/yamlfix b/test/command_callback/python_paths/with_virtualenv/env/bin/yamlfix new file mode 100755 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/python_paths/with_virtualenv/env/bin/yamlfix diff --git a/test/command_callback/r_paths/.Rprofile b/test/command_callback/r_paths/.Rprofile new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/r_paths/.Rprofile diff --git a/test/command_callback/spectral_paths/node_modules/.bin/spectral b/test/command_callback/spectral_paths/node_modules/.bin/spectral new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/spectral_paths/node_modules/.bin/spectral diff --git a/test/command_callback/spectral_paths/openapi.yaml b/test/command_callback/spectral_paths/openapi.yaml new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/spectral_paths/openapi.yaml diff --git a/test/command_callback/test_adals_command_callbacks.vader b/test/command_callback/test_adals_command_callbacks.vader new file mode 100644 index 00000000..5a04594e --- /dev/null +++ b/test/command_callback/test_adals_command_callbacks.vader @@ -0,0 +1,17 @@ +Before: + call ale#assert#SetUpLinterTest('ada', 'adals') + +After: + call ale#assert#TearDownLinterTest() + +Execute(Sets adals executable): + let g:ale_ada_adals_executable = '/path/to /Ada' + AssertLinter '/path/to /Ada', ale#Escape('/path/to /Ada') + +Execute(Sets adals encoding): + let b:ale_ada_adals_encoding = 'iso-8859-1' + AssertLSPConfig {'ada.defaultCharset': 'iso-8859-1', 'ada.projectFile': 'default.gpr'} + +Execute(Sets adals project): + let g:ale_ada_adals_project = 'myproject.gpr' + AssertLSPConfig {'ada.defaultCharset': 'utf-8', 'ada.projectFile': 'myproject.gpr'} diff --git a/test/command_callback/test_clang_tidy_command_callback.vader b/test/command_callback/test_clang_tidy_command_callback.vader index f0a07e8c..eb1220be 100644 --- a/test/command_callback/test_clang_tidy_command_callback.vader +++ b/test/command_callback/test_clang_tidy_command_callback.vader @@ -68,7 +68,6 @@ Execute(The build directory should be used for header files): \ ale#Escape('clang-tidy') \ . ' -checks=' . ale#Escape('*') . ' %s' \ . ' -p ' . ale#Escape('/foo/bar') - \ . ' -- -x c++' call ale#test#SetFilename('test.hpp') diff --git a/test/command_callback/test_dart_analysis_server_command_callback.vader b/test/command_callback/test_dart_analysis_server_command_callback.vader new file mode 100644 index 00000000..1754109a --- /dev/null +++ b/test/command_callback/test_dart_analysis_server_command_callback.vader @@ -0,0 +1,15 @@ +Before: + call ale#assert#SetUpLinterTest('dart', 'analysis_server') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct): + AssertLinter 'dart', ale#Escape('dart') + \ . ' ./snapshots/analysis_server.dart.snapshot --lsp' + +Execute(The executable should be configurable): + let g:ale_dart_analysis_server_executable = 'foobar' + + AssertLinter 'foobar', ale#Escape('foobar') + \ . ' ./snapshots/analysis_server.dart.snapshot --lsp' diff --git a/test/command_callback/test_elixir_credo.vader b/test/command_callback/test_elixir_credo.vader index 3eb88846..b14444c6 100644 --- a/test/command_callback/test_elixir_credo.vader +++ b/test/command_callback/test_elixir_credo.vader @@ -38,3 +38,10 @@ Execute(Builds credo command with suggest mode when set to 0): AssertLinter 'mix', \ ale#path#CdString(ale#path#Simplify(g:dir . '/elixir_paths/mix_project')) \ . 'mix help credo && mix credo suggest --format=flycheck --read-from-stdin %s' + +Execute(Builds credo command with a custom config file): + let g:ale_elixir_credo_config_file = '/home/user/custom_credo.exs' + + AssertLinter 'mix', + \ ale#path#CdString(ale#path#Simplify(g:dir . '/elixir_paths/mix_project')) + \ . 'mix help credo && mix credo suggest --config-file /home/user/custom_credo.exs --format=flycheck --read-from-stdin %s' diff --git a/test/command_callback/test_erlang_dialyzer_command_callback.vader b/test/command_callback/test_erlang_dialyzer_command_callback.vader index 5e21c053..5e818d7f 100644 --- a/test/command_callback/test_erlang_dialyzer_command_callback.vader +++ b/test/command_callback/test_erlang_dialyzer_command_callback.vader @@ -25,6 +25,14 @@ Execute(The command should accept configured executable.): \ . ' -Wunderspecs' \ . ' %s' +Execute(The command should accept configured options.): + let b:ale_erlang_dialyzer_options = '-r ' . expand('$HOME') + AssertLinter 'dialyzer', + \ ale#Escape('dialyzer') + \ . ' -n --plt ' . ale#Escape(expand('$HOME/.dialyzer_plt')) + \ . ' -r ' . expand('$HOME') + \ . ' %s' + Execute(The command should accept configured PLT file.): let b:ale_erlang_dialyzer_plt_file = 'custom-plt' AssertLinter 'dialyzer', diff --git a/test/command_callback/test_erlang_elvis_command_callback.vader b/test/command_callback/test_erlang_elvis_command_callback.vader new file mode 100644 index 00000000..4aab49d6 --- /dev/null +++ b/test/command_callback/test_erlang_elvis_command_callback.vader @@ -0,0 +1,16 @@ +Before: + let b:file = fnamemodify(bufname(''), ':.') + call ale#assert#SetUpLinterTest('erlang', 'elvis') + +After: + call ale#assert#TearDownLinterTest() + +Execute(Default command should be correct): + AssertLinter 'elvis', + \ ale#Escape('elvis') . ' rock --output-format=parsable ' . ale#Escape(b:file) + +Execute(Executable should be configurable): + let b:ale_erlang_elvis_executable = '/path/to/elvis' + + AssertLinter '/path/to/elvis', + \ ale#Escape('/path/to/elvis') . ' rock --output-format=parsable ' . ale#Escape(b:file) diff --git a/test/command_callback/test_erlang_erlc_command_callback.vader b/test/command_callback/test_erlang_erlc_command_callback.vader new file mode 100644 index 00000000..7d659a07 --- /dev/null +++ b/test/command_callback/test_erlang_erlc_command_callback.vader @@ -0,0 +1,40 @@ +Before: + call ale#assert#SetUpLinterTest('erlang', 'erlc') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct.): + let g:cmd = ale_linters#erlang#erlc#GetCommand(bufnr('')) + let g:regex = 'erlc.\+-o.\+%t' + let g:matched = match(g:cmd, g:regex) + + " match returns -1 if not found + AssertNotEqual + \ g:matched, + \ -1, + \ 'Command error: expected [' . g:cmd . '] to match [' . g:regex . ']' + +Execute(The command should accept configured executable.): + let b:ale_erlang_erlc_executable = '/usr/bin/erlc' + let g:cmd = ale_linters#erlang#erlc#GetCommand(bufnr('')) + let g:regex = '/usr/bin/erlc.\+-o.\+%t' + let g:matched = match(g:cmd, g:regex) + + " match returns -1 if not found + AssertNotEqual + \ g:matched, + \ -1, + \ 'Command error: expected [' . g:cmd . '] to match [' . g:regex . ']' + +Execute(The command should accept configured options.): + let b:ale_erlang_erlc_options = '-I include' + let g:cmd = ale_linters#erlang#erlc#GetCommand(bufnr('')) + let g:regex = 'erlc.\+-o.\+-I include.\+%t' + let g:matched = match(g:cmd, g:regex) + + " match returns -1 if not found + AssertNotEqual + \ g:matched, + \ -1, + \ 'Command error: expected [' . g:cmd . '] to match [' . g:regex . ']' diff --git a/test/command_callback/test_fecs_command_callback.vader b/test/command_callback/test_fecs_command_callback.vader index f70ad084..4287d324 100644 --- a/test/command_callback/test_fecs_command_callback.vader +++ b/test/command_callback/test_fecs_command_callback.vader @@ -1,5 +1,6 @@ Before: call ale#assert#SetUpLinterTest('javascript', 'fecs') + runtime autoload/ale/handlers/fecs.vim After: call ale#assert#TearDownLinterTest() diff --git a/test/command_callback/test_haskell_hls_callbacks.vader b/test/command_callback/test_haskell_hls_callbacks.vader new file mode 100644 index 00000000..e64aab6f --- /dev/null +++ b/test/command_callback/test_haskell_hls_callbacks.vader @@ -0,0 +1,27 @@ +Before: + call ale#assert#SetUpLinterTest('haskell', 'hls') + + Save &filetype + let &filetype = 'haskell' + +After: + call ale#assert#TearDownLinterTest() + +Execute(The language string should be correct): + AssertLSPLanguage 'haskell' + +Execute(The default executable should be correct): + AssertLinter 'haskell-language-server-wrapper', + \ ale#Escape('haskell-language-server-wrapper') . ' --lsp' + +Execute(The project root should be detected correctly): + AssertLSPProject g:dir + + call ale#test#SetFilename('hls_paths/file.hs') + + AssertLSPProject ale#path#Simplify(g:dir . '/hls_paths') + +Execute(The executable should be configurable): + let g:ale_haskell_hls_executable = 'foobar' + + AssertLinter 'foobar', ale#Escape('foobar') . ' --lsp' diff --git a/test/command_callback/test_ibm_openapi_validator_command_callback.vader b/test/command_callback/test_ibm_openapi_validator_command_callback.vader new file mode 100644 index 00000000..3484cc09 --- /dev/null +++ b/test/command_callback/test_ibm_openapi_validator_command_callback.vader @@ -0,0 +1,15 @@ +Before: + call ale#assert#SetUpLinterTest('openapi', 'ibm_validator') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The yaml ibm-openapi-validator command callback should return the correct default string): + AssertLinter 'lint-openapi', ale#Escape('lint-openapi') . ' %t' + +Execute(The yaml ibm-openapi-validator command callback should be configurable): + let g:ale_openapi_ibm_validator_executable = '~/.local/bin/lint-openapi' + let g:ale_openapi_ibm_validator_options = '-c ~/.config' + + AssertLinter '~/.local/bin/lint-openapi', ale#Escape('~/.local/bin/lint-openapi') + \ . ' -c ~/.config %t' diff --git a/test/command_callback/test_inko_inko_callbacks.vader b/test/command_callback/test_inko_inko_callbacks.vader new file mode 100644 index 00000000..93295c91 --- /dev/null +++ b/test/command_callback/test_inko_inko_callbacks.vader @@ -0,0 +1,20 @@ +Before: + call ale#assert#SetUpLinterTest('inko', 'inko') + call ale#test#SetFilename('inko_paths/test.inko') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default executable path should be correct): + AssertLinter 'inko', ale#Escape('inko') . ' build --check --format=json %s' + +Execute(The inko callback should include tests/ for test paths): + call ale#engine#Cleanup(bufnr('')) + noautocmd e! inko_paths/tests/test/test_foo.inko + call ale#engine#InitBufferInfo(bufnr('')) + + AssertLinter 'inko', + \ ale#Escape('inko') + \ . ' build --check --format=json --include ' + \ . ale#Escape(ale#path#Simplify(g:dir . '/inko_paths/tests/')) + \ . ' %s' diff --git a/test/command_callback/test_julia_languageserver_callbacks.vader b/test/command_callback/test_julia_languageserver_callbacks.vader index 3bc46e3d..96df81f1 100644 --- a/test/command_callback/test_julia_languageserver_callbacks.vader +++ b/test/command_callback/test_julia_languageserver_callbacks.vader @@ -11,16 +11,16 @@ After: Execute(The default executable path should be correct): AssertLinter 'julia', \ ale#Escape('julia') . - \' --startup-file=no --history-file=no -e ' . - \ ale#Escape('using LanguageServer; server = LanguageServer.LanguageServerInstance(isdefined(Base, :stdin) ? stdin : STDIN, isdefined(Base, :stdout) ? stdout : STDOUT, false); server.runlinter = true; run(server);') + \' --project=@. --startup-file=no --history-file=no -e ' . + \ ale#Escape('using LanguageServer; using Pkg; import StaticLint; import SymbolServer; server = LanguageServer.LanguageServerInstance(isdefined(Base, :stdin) ? stdin : STDIN, isdefined(Base, :stdout) ? stdout : STDOUT, dirname(Pkg.Types.Context().env.project_file)); server.runlinter = true; run(server);') Execute(The executable should be configurable): let g:ale_julia_executable = 'julia-new' AssertLinter 'julia-new', \ ale#Escape('julia-new') . - \' --startup-file=no --history-file=no -e ' . - \ ale#Escape('using LanguageServer; server = LanguageServer.LanguageServerInstance(isdefined(Base, :stdin) ? stdin : STDIN, isdefined(Base, :stdout) ? stdout : STDOUT, false); server.runlinter = true; run(server);') + \' --project=@. --startup-file=no --history-file=no -e ' . + \ ale#Escape('using LanguageServer; using Pkg; import StaticLint; import SymbolServer; server = LanguageServer.LanguageServerInstance(isdefined(Base, :stdin) ? stdin : STDIN, isdefined(Base, :stdout) ? stdout : STDOUT, dirname(Pkg.Types.Context().env.project_file)); server.runlinter = true; run(server);') Execute(The project root should be detected correctly): AssertLSPProject '' diff --git a/test/command_callback/test_php_intelephense_command_callback.vader b/test/command_callback/test_php_intelephense_command_callback.vader new file mode 100644 index 00000000..dd6adb3d --- /dev/null +++ b/test/command_callback/test_php_intelephense_command_callback.vader @@ -0,0 +1,26 @@ +Before: + call ale#assert#SetUpLinterTest('php', 'intelephense') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default executable path should be correct): + AssertLinter 'intelephense', + \ ale#Escape('intelephense') . ' --stdio' + +Execute(The project path should be correct for .git directories): + call ale#test#SetFilename('php-intelephense-project/with-git/test.php') + silent! call mkdir('php-intelephense-project/with-git/.git', 'p') + + AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-git') + +Execute(The project path should be correct for composer.json file): + call ale#test#SetFilename('php-intelephense-project/with-composer/test.php') + + AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-composer') + +Execute(The project cache should be saved in a temp dir): + call ale#test#SetFilename('php-intelephense-project/with-composer/test.php') + let g:ale_php_intelephense_config = { 'storagePath': '/tmp/intelephense' } + + AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-composer') diff --git a/test/command_callback/test_r_languageserver_callbacks.vader b/test/command_callback/test_r_languageserver_callbacks.vader new file mode 100644 index 00000000..9a4a1f87 --- /dev/null +++ b/test/command_callback/test_r_languageserver_callbacks.vader @@ -0,0 +1,22 @@ +Before: + call ale#assert#SetUpLinterTest('r', 'languageserver') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default executable path should be correct): + AssertLinter 'Rscript', 'Rscript --vanilla -e ' . ale#Escape('languageserver::run()') + +Execute(The project root should be detected correctly): + AssertLSPProject '.' + + call ale#test#SetFilename('r_paths/dummy/test.R') + + AssertLSPProject ale#path#Simplify(g:dir . '/r_paths') + +Execute(Should accept configuration settings): + AssertLSPConfig {} + + let b:ale_r_languageserver_config = {'r': {'lsp': {'debug': 'true', 'diagnostics': 'true'}}} + + AssertLSPConfig {'r': {'lsp': {'debug': 'true', 'diagnostics': 'true'}}} diff --git a/test/command_callback/test_rust_analyzer_callbacks.vader b/test/command_callback/test_rust_analyzer_callbacks.vader index 95866076..efab1378 100644 --- a/test/command_callback/test_rust_analyzer_callbacks.vader +++ b/test/command_callback/test_rust_analyzer_callbacks.vader @@ -16,5 +16,5 @@ Execute(The project root should be detected correctly): Execute(Should accept configuration settings): AssertLSPConfig {} - let b:ale_rust_analyzer_config = {'rust': {'clippy_preference': 'on'}} - AssertLSPConfig {'rust': {'clippy_preference': 'on'}} + let b:ale_rust_analyzer_config = {'diagnostics': {'disabled': ['unresolved-import']}} + AssertLSPOptions {'diagnostics': {'disabled': ['unresolved-import']}} diff --git a/test/command_callback/test_sorbet_command_callback.vader b/test/command_callback/test_sorbet_command_callback.vader index b46e90a4..fe758635 100644 --- a/test/command_callback/test_sorbet_command_callback.vader +++ b/test/command_callback/test_sorbet_command_callback.vader @@ -5,6 +5,7 @@ Before: let g:ale_ruby_sorbet_executable = 'srb' let g:ale_ruby_sorbet_options = '' + let g:ale_ruby_sorbet_enable_watchman = 0 After: call ale#assert#TearDownLinterTest() @@ -13,6 +14,12 @@ Execute(Executable should default to srb): AssertLinter 'srb', ale#Escape('srb') \ . ' tc --lsp --disable-watchman' +Execute(Able to enable watchman): + let g:ale_ruby_sorbet_enable_watchman = 1 + + AssertLinter 'srb', ale#Escape('srb') + \ . ' tc --lsp' + Execute(Should be able to set a custom executable): let g:ale_ruby_sorbet_executable = 'bin/srb' diff --git a/test/command_callback/test_spectral_command_callback.vader b/test/command_callback/test_spectral_command_callback.vader new file mode 100644 index 00000000..ed3795b9 --- /dev/null +++ b/test/command_callback/test_spectral_command_callback.vader @@ -0,0 +1,31 @@ +Before: + call ale#assert#SetUpLinterTest('yaml', 'spectral') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The yaml spectral command callback should return the correct default string): + AssertLinter 'spectral', ale#Escape('spectral') . ' lint --ignore-unknown-format -q -f text %t' + +Execute(The yaml spectral command callback should be configurable): + let g:ale_yaml_spectral_executable = '~/.local/bin/spectral' + + AssertLinter '~/.local/bin/spectral', + \ ale#Escape('~/.local/bin/spectral') + \ . ' lint --ignore-unknown-format -q -f text %t' + +Execute(The yaml spectral command callback should allow a global installation to be used): + let g:ale_yaml_spectral_executable = '/usr/local/bin/spectral' + let g:ale_yaml_spectral_use_global = 1 + + AssertLinter '/usr/local/bin/spectral', + \ ale#Escape('/usr/local/bin/spectral') + \ . ' lint --ignore-unknown-format -q -f text %t' + +Execute(The yaml spectral command callback should allow a local installation to be used): + call ale#test#SetFilename('spectral_paths/openapi.yaml') + + AssertLinter + \ ale#path#Simplify(g:dir . '/spectral_paths/node_modules/.bin/spectral'), + \ ale#Escape(ale#path#Simplify(g:dir . '/spectral_paths/node_modules/.bin/spectral')) + \ . ' lint --ignore-unknown-format -q -f text %t' diff --git a/test/command_callback/test_terraform_ls_command_callback.vader b/test/command_callback/test_terraform_ls_command_callback.vader new file mode 100644 index 00000000..d559ba8c --- /dev/null +++ b/test/command_callback/test_terraform_ls_command_callback.vader @@ -0,0 +1,61 @@ +Before: + call ale#assert#SetUpLinterTest('terraform', 'terraform_ls') + +After: + if isdirectory(g:dir . '/.terraform') + call delete(g:dir . '/.terraform', 'd') + endif + + unlet! b:ale_terraform_terraform_executable + unlet! b:ale_terraform_ls_executable + unlet! b:ale_terraform_ls_options + + call ale#assert#TearDownLinterTest() + +Execute(Should send correct LSP language): + AssertLSPLanguage 'terraform' + +Execute(Should load default executable): + AssertLinter 'terraform-ls', + \ ale#Escape('terraform-ls') . ' serve' + +Execute(Should configure custom executable): + let b:ale_terraform_ls_executable = 'foo' + AssertLinter 'foo', + \ ale#Escape('foo') . ' serve' + +Execute(Should ignore non-absolute custom terraform executable): + let b:ale_terraform_terraform_executable = 'terraform' + AssertLinter 'terraform-ls', + \ ale#Escape('terraform-ls') . ' serve' + +Execute(Should set absolute custom terraform executable): + let b:ale_terraform_terraform_executable = '/bin/terraform' + AssertLinter 'terraform-ls', + \ ale#Escape('terraform-ls') . ' serve -tf-exec /bin/terraform' + +Execute(Should set custom options): + let b:ale_terraform_ls_options = '--bar' + + AssertLinter 'terraform-ls', + \ ale#Escape('terraform-ls') . ' serve --bar' + +Execute(Should return current directory if it contains .terraform directory): + call mkdir(g:dir . '/.terraform') + AssertLSPProject g:dir + +Execute(Should return nearest directory with .terraform if found in parent directory): + call ale#test#SetFilename('../terraform_files/main.tf') + + let b:parent_dir = ale#path#Simplify(g:dir . '/..') + let b:tf_dir = b:parent_dir . '/.terraform' + + if !isdirectory(b:tf_dir) + call mkdir(b:tf_dir) + endif + + AssertLSPProject b:parent_dir + + call delete(b:tf_dir, 'd') + unlet!b:parent_dir + unlet!b:tf_dir diff --git a/test/command_callback/test_terraform_terraform_command_callback.vader b/test/command_callback/test_terraform_terraform_command_callback.vader index fabd902d..25ca652a 100644 --- a/test/command_callback/test_terraform_terraform_command_callback.vader +++ b/test/command_callback/test_terraform_terraform_command_callback.vader @@ -6,4 +6,10 @@ After: call ale#assert#TearDownLinterTest() Execute(The default command should be correct): - AssertLinter 'terraform', ale#Escape('terraform') . ' fmt -no-color --check=true -' + AssertLinter 'terraform', + \ ale#Escape('terraform') . ' validate -no-color -json ' + +Execute(The default command should be overriden): + let b:ale_terraform_terraform_executable = '/bin/other/terraform' + AssertLinter '/bin/other/terraform', + \ ale#Escape('/bin/other/terraform') . ' validate -no-color -json ' diff --git a/test/command_callback/test_typescript_deno_lsp.vader b/test/command_callback/test_typescript_deno_lsp.vader new file mode 100644 index 00000000..01cbc851 --- /dev/null +++ b/test/command_callback/test_typescript_deno_lsp.vader @@ -0,0 +1,60 @@ +Before: + let g:ale_deno_unstable = 0 + let g:ale_deno_executable = 'deno' + let g:ale_deno_project_root = '' + + runtime autoload/ale/handlers/deno.vim + call ale#assert#SetUpLinterTest('typescript', 'deno') + +After: + call ale#assert#TearDownLinterTest() + +Execute(Should set deno lsp for TypeScript projects using stable Deno API): + AssertLSPLanguage 'typescript' + AssertLSPConfig {} + AssertLSPProject ale#path#Simplify(g:dir . '/../..') + AssertLSPOptions { + \ 'enable': v:true, + \ 'lint': v:true, + \ 'unstable': v:false + \} + +Execute(Should set deno lsp using unstable Deno API if enabled by user): + let g:ale_deno_unstable = 1 + AssertLSPLanguage 'typescript' + AssertLSPConfig {} + AssertLSPProject ale#path#Simplify(g:dir . '/../..') + AssertLSPOptions { + \ 'enable': v:true, + \ 'lint': v:true, + \ 'unstable': v:true + \} + +Execute(Should find project root containing tsconfig.json): + call ale#test#SetFilename('../typescript/test.ts') + AssertLSPLanguage 'typescript' + AssertLSPConfig {} + AssertLSPProject ale#path#Simplify(g:dir . '/../typescript') + AssertLSPOptions { + \ 'enable': v:true, + \ 'lint': v:true, + \ 'unstable': v:false + \} + +Execute(Should use user-specified project root): + let g:ale_deno_lsp_project_root = '/' + + call ale#test#SetFilename('../typescript/test.ts') + AssertLSPLanguage 'typescript' + AssertLSPConfig {} + AssertLSPProject '/' + AssertLSPOptions { + \ 'enable': v:true, + \ 'lint': v:true, + \ 'unstable': v:false + \} + +Execute(Check Deno LSP command): + AssertLinter 'deno', [ + \ ale#Escape('deno') . ' lsp', + \] diff --git a/test/command_callback/test_xo_command_callback.vader b/test/command_callback/test_xo_command_callback.vader index 65cb4f8a..1aa4c3f1 100644 --- a/test/command_callback/test_xo_command_callback.vader +++ b/test/command_callback/test_xo_command_callback.vader @@ -1,8 +1,11 @@ Before: - call ale#assert#SetUpLinterTest('typescript', 'xo') - call ale#test#SetFilename('testfile.ts') + call ale#assert#SetUpLinterTest('javascript', 'xo') + call ale#test#SetFilename('testfile.jsx') unlet! b:executable + set filetype=javascriptreact + runtime autoload/ale/handlers/xo.vim + After: call ale#assert#TearDownLinterTest() @@ -10,11 +13,11 @@ Execute(The XO executable should be called): AssertLinter 'xo', ale#Escape('xo') . ' --reporter json --stdin --stdin-filename %s' Execute(The XO executable should be configurable): - let b:ale_typescript_xo_executable = 'foobar' + let b:ale_javascript_xo_executable = 'foobar' AssertLinter 'foobar', ale#Escape('foobar') . ' --reporter json --stdin --stdin-filename %s' Execute(The XO options should be configurable): - let b:ale_typescript_xo_options = '--wat' + let b:ale_javascript_xo_options = '--wat' AssertLinter 'xo', ale#Escape('xo') . ' --wat --reporter json --stdin --stdin-filename %s' diff --git a/test/command_callback/test_xots_command_callback.vader b/test/command_callback/test_xots_command_callback.vader new file mode 100644 index 00000000..cc38ff02 --- /dev/null +++ b/test/command_callback/test_xots_command_callback.vader @@ -0,0 +1,23 @@ +Before: + call ale#assert#SetUpLinterTest('typescript', 'xo') + call ale#test#SetFilename('testfile.tsx') + unlet! b:executable + + set filetype=typescriptreact + runtime autoload/ale/handlers/xo.vim + +After: + call ale#assert#TearDownLinterTest() + +Execute(The XO executable should be called): + AssertLinter 'xo', ale#Escape('xo') . ' --reporter json --stdin --stdin-filename %s' + +Execute(The XO executable should be configurable): + let b:ale_typescript_xo_executable = 'foobar' + + AssertLinter 'foobar', ale#Escape('foobar') . ' --reporter json --stdin --stdin-filename %s' + +Execute(The XO options should be configurable): + let b:ale_typescript_xo_options = '--wat' + + AssertLinter 'xo', ale#Escape('xo') . ' --wat --reporter json --stdin --stdin-filename %s' diff --git a/test/completion/test_lsp_completion_parsing.vader b/test/completion/test_lsp_completion_parsing.vader index d989aefe..a334d945 100644 --- a/test/completion/test_lsp_completion_parsing.vader +++ b/test/completion/test_lsp_completion_parsing.vader @@ -10,36 +10,39 @@ After: unlet! b:ale_completion_info Execute(Should handle Rust completion results correctly): + let g:ale_completion_autoimport = 0 + AssertEqual \ [ - \ {'word': 'new', 'menu': 'pub fn new() -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'with_capacity', 'menu': 'pub fn with_capacity(capacity: usize) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_utf8', 'menu': 'pub fn from_utf8(vec: Vec<u8>) -> Result<String, FromUtf8Error>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_utf8_lossy', 'menu': 'pub fn from_utf8_lossy<''a>(v: &''a [u8]) -> Cow<''a, str>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_utf16', 'menu': 'pub fn from_utf16(v: &[u16]) -> Result<String, FromUtf16Error>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_utf16_lossy', 'menu': 'pub fn from_utf16_lossy(v: &[u16]) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_raw_parts', 'menu': 'pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_utf8_unchecked', 'menu': 'pub unsafe fn from_utf8_unchecked(bytes: Vec<u8>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = &''a char>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = &''a str>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_iter', 'menu': 'fn from_iter<I: IntoIterator<Item = Cow<''a, str>>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'Searcher', 'menu': 'type Searcher = <&''b str as Pattern<''a>>::Searcher;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'default', 'menu': 'fn default() -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'Output', 'menu': 'type Output = String;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'Target', 'menu': 'type Target = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'Err', 'menu': 'type Err = ParseError;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from_str', 'menu': 'fn from_str(s: &str) -> Result<String, ParseError>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from', 'menu': 'fn from(s: &''a str) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from', 'menu': 'fn from(s: Box<str>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'from', 'menu': 'fn from(s: Cow<''a, str>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'new', 'dup': 0, 'menu': 'pub fn new() -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'with_capacity', 'dup': 0, 'menu': 'pub fn with_capacity(capacity: usize) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_utf8', 'dup': 0, 'menu': 'pub fn from_utf8(vec: Vec<u8>) -> Result<String, FromUtf8Error>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_utf8_lossy', 'dup': 0, 'menu': 'pub fn from_utf8_lossy<''a>(v: &''a [u8]) -> Cow<''a, str>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_utf16', 'dup': 0, 'menu': 'pub fn from_utf16(v: &[u16]) -> Result<String, FromUtf16Error>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_utf16_lossy', 'dup': 0, 'menu': 'pub fn from_utf16_lossy(v: &[u16]) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_raw_parts', 'dup': 0, 'menu': 'pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_utf8_unchecked', 'dup': 0, 'menu': 'pub unsafe fn from_utf8_unchecked(bytes: Vec<u8>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_iter', 'dup': 0, 'menu': 'fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_iter', 'dup': 0, 'menu': 'fn from_iter<I: IntoIterator<Item = &''a char>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_iter', 'dup': 0, 'menu': 'fn from_iter<I: IntoIterator<Item = &''a str>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_iter', 'dup': 0, 'menu': 'fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_iter', 'dup': 0, 'menu': 'fn from_iter<I: IntoIterator<Item = Cow<''a, str>>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'Searcher', 'dup': 0, 'menu': 'type Searcher = <&''b str as Pattern<''a>>::Searcher;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'default', 'dup': 0, 'menu': 'fn default() -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'Output', 'dup': 0, 'menu': 'type Output = String;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'Output', 'dup': 0, 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'Output', 'dup': 0, 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'Output', 'dup': 0, 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'Output', 'dup': 0, 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'Output', 'dup': 0, 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'Output', 'dup': 0, 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'Target', 'dup': 0, 'menu': 'type Target = str;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'Err', 'dup': 0, 'menu': 'type Err = ParseError;', 'info': '', 'kind': 't', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from_str', 'dup': 0, 'menu': 'fn from_str(s: &str) -> Result<String, ParseError>', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from', 'dup': 0, 'menu': 'fn from(s: &''a str) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from', 'dup': 0, 'menu': 'fn from(s: Box<str>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'from', 'dup': 0, 'menu': 'fn from(s: Cow<''a, str>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'to_vec', 'dup': 0, 'menu': 'pub fn to_vec(&self) -> Vec<T> where T: Clone,', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, \], \ ale#completion#ParseLSPCompletions({ \ "jsonrpc":"2.0", @@ -184,18 +187,24 @@ Execute(Should handle Rust completion results correctly): \ "label":"from", \ "kind":3, \ "detail":"fn from(s: Cow<'a, str>) -> String" + \ }, + \ { + \ "label":"to_vec", + \ "kind":3, + \ "detail":"pub fn to_vec(&self) -> Vec<T>\nwhere\n T: Clone," \ } \ ] \ }) Execute(Should handle Python completion results correctly): + let g:ale_completion_autoimport = 0 let b:ale_completion_info = { \ 'completion_filter': 'ale#completion#python#CompletionItemFilter', \} AssertEqual \ [ - \ {'word': 'what', 'menu': 'example-python-project.bar.Bar', 'info': "what()\n\n", 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'what', 'dup': 0, 'menu': 'example-python-project.bar.Bar', 'info': "what()\n\n", 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, \ ], \ ale#completion#ParseLSPCompletions({ \ "jsonrpc":"2.0", @@ -400,6 +409,8 @@ Execute(Should handle Python completion results correctly): \ }) Execute(Should handle extra Python completion results correctly): + let g:ale_completion_autoimport = 0 + let b:ale_completion_info = { \ 'completion_filter': 'ale#completion#python#CompletionItemFilter', \ 'prefix': 'mig', @@ -407,8 +418,8 @@ Execute(Should handle extra Python completion results correctly): AssertEqual \ [ - \ {'word': 'migrations', 'menu': 'xxx', 'info': 'migrations', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, - \ {'word': 'MigEngine', 'menu': 'xxx', 'info': 'mig engine', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'migrations', 'dup': 0, 'menu': 'xxx', 'info': 'migrations', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'MigEngine', 'dup': 0, 'menu': 'xxx', 'info': 'mig engine', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, \ ], \ ale#completion#ParseLSPCompletions({ \ 'jsonrpc': '2.0', @@ -439,9 +450,11 @@ Execute(Should handle extra Python completion results correctly): \ }) Execute(Should handle missing keys): + let g:ale_completion_autoimport = 0 + AssertEqual \ [ - \ {'word': 'x', 'menu': '', 'info': '', 'kind': 'v', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'x', 'dup': 0, 'menu': '', 'info': '', 'kind': 'v', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, \ ], \ ale#completion#ParseLSPCompletions({ \ 'jsonrpc': '2.0', @@ -457,9 +470,11 @@ Execute(Should handle missing keys): \ }) Execute(Should handle documentation in the markdown format): + let g:ale_completion_autoimport = 0 + AssertEqual \ [ - \ {'word': 'migrations', 'menu': 'xxx', 'info': 'Markdown documentation', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'migrations', 'dup': 0, 'menu': 'xxx', 'info': 'Markdown documentation', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, \ ], \ ale#completion#ParseLSPCompletions({ \ 'jsonrpc': '2.0', @@ -481,9 +496,11 @@ Execute(Should handle documentation in the markdown format): \ }) Execute(Should handle completion messages with textEdit objects): + let g:ale_completion_autoimport = 0 + AssertEqual \ [ - \ {'word': 'next_callback', 'menu': 'PlayTimeCallback', 'info': '', 'kind': 'v', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'next_callback', 'dup': 0, 'menu': 'PlayTimeCallback', 'info': '', 'kind': 'v', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, \ ], \ ale#completion#ParseLSPCompletions({ \ 'id': 226, @@ -512,9 +529,11 @@ Execute(Should handle completion messages with textEdit objects): \ }) Execute(Should handle completion messages with the deprecated insertText attribute): + let g:ale_completion_autoimport = 0 + AssertEqual \ [ - \ {'word': 'next_callback', 'menu': 'PlayTimeCallback', 'info': '', 'kind': 'v', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'next_callback', 'dup': 0, 'menu': 'PlayTimeCallback', 'info': '', 'kind': 'v', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, \ ], \ ale#completion#ParseLSPCompletions({ \ 'id': 226, @@ -542,6 +561,7 @@ Execute(Should handle completion messages with additionalTextEdits when ale_comp \ [ \ { \ 'word': 'next_callback', + \ 'dup': 1, \ 'menu': 'PlayTimeCallback', \ 'info': '', \ 'kind': 'v', @@ -655,6 +675,7 @@ Execute(Should still handle completion messages with empty additionalTextEdits w \ [ \ { \ 'word': 'next_callback', + \ 'dup': 0, \ 'menu': 'PlayTimeCallback', \ 'info': '', \ 'kind': 'v', diff --git a/test/completion/test_omnifunc_completion.vader b/test/completion/test_omnifunc_completion.vader index 3711f974..1db64705 100644 --- a/test/completion/test_omnifunc_completion.vader +++ b/test/completion/test_omnifunc_completion.vader @@ -45,15 +45,15 @@ Execute(The start position should be returned when results can be requested): AssertEqual 11, ale#completion#OmniFunc(1, '') Execute(The omnifunc function should return async results): - " Neovim 0.2.0 struggles at running these tests. - if !has('nvim') || has('nvim-0.3.0') + " Neovim 0.2.0 and 0.4.4 struggles at running these tests. + if !has('nvim') call timer_start(0, function('SetCompletionResult')) AssertEqual ['foo'], ale#completion#OmniFunc(0, '') endif Execute(The omnifunc function should parse and return async responses): - if !has('nvim') || has('nvim-0.3.0') + if !has('nvim') call timer_start(0, function('SetCompletionResponse')) AssertEqual ['foo'], ale#completion#OmniFunc(0, '') diff --git a/test/dumb_named_pipe_server.py b/test/dumb_named_pipe_server.py new file mode 100644 index 00000000..a77e538c --- /dev/null +++ b/test/dumb_named_pipe_server.py @@ -0,0 +1,42 @@ +""" +This Python script creates a named pipe server that does nothing but send its input +back to the client that connects to it. Only one argument must be given, the path +of a named pipe to bind to. +""" +import os +import socket +import sys + + +def main(): + if len(sys.argv) < 2: + sys.exit('You must specify a filepath') + + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + if os.path.exists(sys.argv[1]): + os.remove(sys.argv[1]) + sock.bind(sys.argv[1]) + sock.listen(0) + + pid = os.fork() + + if pid: + print(pid) + sys.exit() + + while True: + connection = sock.accept()[0] + connection.settimeout(5) + + while True: + try: + connection.send(connection.recv(1024)) + except socket.timeout: + break + + connection.close() + + +if __name__ == "__main__": + main() diff --git a/test/eslint-test-files/react-app/node_modules/xo/cli.js b/test/eslint-test-files/react-app/node_modules/xo/cli.js new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/eslint-test-files/react-app/node_modules/xo/cli.js diff --git a/test/eslint-test-files/react-app/subdir/testfile.ts b/test/eslint-test-files/react-app/subdir/testfile.ts new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/eslint-test-files/react-app/subdir/testfile.ts diff --git a/test/fixers/test_autoimport_fixer_callback.vader b/test/fixers/test_autoimport_fixer_callback.vader new file mode 100644 index 00000000..6952cbb8 --- /dev/null +++ b/test/fixers/test_autoimport_fixer_callback.vader @@ -0,0 +1,50 @@ +Before: + Save g:ale_python_autoimport_executable + Save g:ale_python_autoimport_options + + " Use an invalid global executable, so we don't match it. + let g:ale_python_autoimport_executable = 'xxxinvalid' + let g:ale_python_autoimport_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + silent cd .. + silent cd command_callback + let g:dir = getcwd() + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + +After: + Restore + + unlet! b:bin_dir + + call ale#test#RestoreDirectory() + +Execute(The autoimport callback should return the correct default values): + AssertEqual + \ 0, + \ ale#fixers#autoimport#Fix(bufnr('')) + + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') + AssertEqual + \ { + \ 'command': ale#path#BufferCdString(bufnr('')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autoimport')) . ' -', + \ }, + \ ale#fixers#autoimport#Fix(bufnr('')) + +Execute(The autoimport callback should respect custom options): + let g:ale_python_autoimport_options = '--multi-line=3 --trailing-comma' + + AssertEqual + \ 0, + \ ale#fixers#autoimport#Fix(bufnr('')) + + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') + AssertEqual + \ { + \ 'command': ale#path#BufferCdString(bufnr('')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autoimport')) + \ . ' --multi-line=3 --trailing-comma -', + \ }, + \ ale#fixers#autoimport#Fix(bufnr('')) diff --git a/test/fixers/test_dhall_fixer_callback.vader b/test/fixers/test_dhall_fixer_callback.vader deleted file mode 100644 index f27880b7..00000000 --- a/test/fixers/test_dhall_fixer_callback.vader +++ /dev/null @@ -1,11 +0,0 @@ -Before: - call ale#assert#SetUpFixerTest('dhall', 'dhall') - -After: - call ale#assert#TearDownFixerTest() - -Execute(The default command should be correct): - AssertFixer - \ { 'read_temporary_file': 1, - \ 'command': ale#Escape('dhall') . ' format --inplace %t' - \ } diff --git a/test/fixers/test_dhall_format_fixer_callback.vader b/test/fixers/test_dhall_format_fixer_callback.vader new file mode 100644 index 00000000..9bc17f7e --- /dev/null +++ b/test/fixers/test_dhall_format_fixer_callback.vader @@ -0,0 +1,24 @@ +Before: + Save g:ale_dhall_executable + Save g:ale_dhall_options + + " Use an invalid global executable, so we don’t match it. + let g:ale_dhall_executable = 'odd-dhall' + let g:ale_dhall_options = '--ascii' + + call ale#assert#SetUpFixerTest('dhall-format', 'dhall-format') + +After: + call ale#assert#TearDownFixerTest() + +Execute(The dhall-format callback should return the correct options): + call ale#test#SetFilename('../dhall_files/testfile.dhall') + + AssertFixer + \ { + \ 'command': ale#Escape('odd-dhall') + \ . ' --ascii' + \ . ' format' + \ . ' --inplace %t', + \ 'read_temporary_file': 1, + \ } diff --git a/test/fixers/test_dhall_freeze_fixer_callback.vader b/test/fixers/test_dhall_freeze_fixer_callback.vader new file mode 100644 index 00000000..c8f820bb --- /dev/null +++ b/test/fixers/test_dhall_freeze_fixer_callback.vader @@ -0,0 +1,24 @@ +Before: + Save g:ale_dhall_executable + Save g:ale_dhall_options + + " Use an invalid global executable, so we don’t match it. + let g:ale_dhall_executable = 'odd-dhall' + let g:ale_dhall_options = '--ascii' + let g:ale_dhall_freeze_options = '--all' + + call ale#assert#SetUpFixerTest('dhall-freeze', 'dhall-freeze') + +After: + call ale#assert#TearDownFixerTest() + +Execute(The dhall-freeze callback should return the correct options): + AssertFixer + \ { + \ 'command': ale#Escape('odd-dhall') + \ . ' --ascii' + \ . ' freeze' + \ . ' --all' + \ . ' --inplace %t', + \ 'read_temporary_file': 1, + \ } diff --git a/test/fixers/test_dhall_lint_fixer_callback.vader b/test/fixers/test_dhall_lint_fixer_callback.vader new file mode 100644 index 00000000..82229363 --- /dev/null +++ b/test/fixers/test_dhall_lint_fixer_callback.vader @@ -0,0 +1,22 @@ +Before: + Save g:ale_dhall_executable + Save g:ale_dhall_options + + " Use an invalid global executable, so we don’t match it. + let g:ale_dhall_executable = 'odd-dhall' + let g:ale_dhall_options = '--ascii' + + call ale#assert#SetUpFixerTest('dhall-lint', 'dhall-lint') + +After: + call ale#assert#TearDownFixerTest() + +Execute(The dhall-lint callback should return the correct options): + AssertFixer + \ { + \ 'command': ale#Escape('odd-dhall') + \ . ' --ascii' + \ . ' lint' + \ . ' --inplace %t', + \ 'read_temporary_file': 1, + \ } diff --git a/test/fixers/test_gofmt_fixer_callback.vader b/test/fixers/test_gofmt_fixer_callback.vader index 16659655..99407173 100644 --- a/test/fixers/test_gofmt_fixer_callback.vader +++ b/test/fixers/test_gofmt_fixer_callback.vader @@ -21,10 +21,7 @@ Execute(The gofmt callback should return the correct default values): AssertEqual \ { - \ 'read_temporary_file': 1, - \ 'command': ale#Escape('xxxinvalid') - \ . ' -l -w' - \ . ' %t', + \ 'command': ale#Escape('xxxinvalid'), \ }, \ ale#fixers#gofmt#Fix(bufnr('')) @@ -35,11 +32,8 @@ Execute(The gofmt callback should include custom gofmt options): AssertEqual \ { - \ 'read_temporary_file': 1, \ 'command': ale#Escape('xxxinvalid') - \ . ' -l -w' - \ . ' ' . g:ale_go_gofmt_options - \ . ' %t', + \ . ' ' . g:ale_go_gofmt_options, \ }, \ ale#fixers#gofmt#Fix(bufnr('')) @@ -50,9 +44,7 @@ Execute(The gofmt callback should support Go environment variables): AssertEqual \ { - \ 'read_temporary_file': 1, \ 'command': ale#Env('GO111MODULE', 'off') - \ . ale#Escape('xxxinvalid') . ' -l -w' - \ . ' %t', + \ . ale#Escape('xxxinvalid') \ }, \ ale#fixers#gofmt#Fix(bufnr('')) diff --git a/test/fixers/test_isort_fixer_callback.vader b/test/fixers/test_isort_fixer_callback.vader index 7f389dcf..3941f6dd 100644 --- a/test/fixers/test_isort_fixer_callback.vader +++ b/test/fixers/test_isort_fixer_callback.vader @@ -5,6 +5,7 @@ Before: " Use an invalid global executable, so we don't match it. let g:ale_python_isort_executable = 'xxxinvalid' let g:ale_python_isort_options = '' + let g:ale_python_isort_auto_pipenv = 0 call ale#test#SetDirectory('/testplugin/test/fixers') silent cd .. @@ -48,3 +49,12 @@ Execute(The isort callback should respect custom options): \ . ' --multi-line=3 --trailing-comma -', \ }, \ ale#fixers#isort#Fix(bufnr('')) + +Execute(Pipenv is detected when python_isort_auto_pipenv is set): + let g:ale_python_isort_auto_pipenv = 1 + + call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py') + + AssertEqual + \ {'command': ale#path#BufferCdString(bufnr('')) . ale#Escape('pipenv') . ' run isort -'}, + \ ale#fixers#isort#Fix(bufnr('')) diff --git a/test/fixers/test_luafmt_fixer_callback.vader b/test/fixers/test_luafmt_fixer_callback.vader new file mode 100644 index 00000000..362da118 --- /dev/null +++ b/test/fixers/test_luafmt_fixer_callback.vader @@ -0,0 +1,35 @@ +Before: + Save g:ale_lua_luafmt_executable + Save g:ale_lua_luafmt_options + + " Use an invalid global executable, so we don't match it. + let g:ale_lua_luafmt_executable = 'xxxinvalid' + let g:ale_lua_luafmt_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The luafmt callback should return the correct default values): + call ale#test#SetFilename('../lua_files/testfile.lua') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') . ' --stdin', + \ }, + \ ale#fixers#luafmt#Fix(bufnr('')) + +Execute(The luafmt callback should include custom luafmt options): + let g:ale_lua_luafmt_options = "--skip-children" + call ale#test#SetFilename('../lua_files/testfile.lua') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') + \ . ' ' . g:ale_lua_luafmt_options + \ . ' --stdin', + \ }, + \ ale#fixers#luafmt#Fix(bufnr('')) diff --git a/test/fixers/test_ormolu_fixer_callback.vader b/test/fixers/test_ormolu_fixer_callback.vader new file mode 100644 index 00000000..8df3fca9 --- /dev/null +++ b/test/fixers/test_ormolu_fixer_callback.vader @@ -0,0 +1,24 @@ +Before: + Save g:ale_haskell_ormolu_executable + Save g:ale_haskell_ormolu_options + +After: + Restore + +Execute(The ormolu callback should return the correct default values): + AssertEqual + \ { + \ 'command': ale#Escape('ormolu') + \ }, + \ ale#fixers#ormolu#Fix(bufnr('')) + +Execute(The ormolu executable and options should be configurable): + let g:ale_nix_nixpkgsfmt_executable = '/path/to/ormolu' + let g:ale_nix_nixpkgsfmt_options = '-h' + + AssertEqual + \ { + \ 'command': ale#Escape('/path/to/ormolu') + \ . ' -h', + \ }, + \ ale#fixers#nixpkgsfmt#Fix(bufnr('')) diff --git a/test/fixers/test_phpcbf_fixer_callback.vader b/test/fixers/test_phpcbf_fixer_callback.vader index 1663c89c..f7bcc2d8 100644 --- a/test/fixers/test_phpcbf_fixer_callback.vader +++ b/test/fixers/test_phpcbf_fixer_callback.vader @@ -5,6 +5,7 @@ Before: let g:ale_php_phpcbf_executable = 'phpcbf_test' let g:ale_php_phpcbf_standard = '' + let g:ale_php_phpcbf_options = '' let g:ale_php_phpcbf_use_global = 0 call ale#test#SetDirectory('/testplugin/test/fixers') @@ -54,6 +55,15 @@ Execute(The phpcbf callback should include the phpcbf_standard option): \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . '--standard=phpcbf_ruleset.xml' . ' -'}, \ ale#fixers#phpcbf#Fix(bufnr('')) +Execute(User provided options should be used): + let g:ale_php_phpcbf_options = '--my-user-provided-option my-value' + call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php') + + AssertEqual + \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . ale#Pad('--my-user-provided-option my-value') . ' -'}, + \ ale#fixers#phpcbf#Fix(bufnr('')) + + Before: Save g:ale_php_phpcbf_executable Save g:ale_php_phpcbf_standard @@ -61,6 +71,7 @@ Before: let g:ale_php_phpcbf_executable = 'phpcbf_test' let g:ale_php_phpcbf_standard = '' + let g:ale_php_phpcbf_options = '' let g:ale_php_phpcbf_use_global = 0 call ale#test#SetDirectory('/testplugin/test/fixers') diff --git a/test/fixers/test_standardrb_fixer_callback.vader b/test/fixers/test_standardrb_fixer_callback.vader index 99234b79..d315651f 100644 --- a/test/fixers/test_standardrb_fixer_callback.vader +++ b/test/fixers/test_standardrb_fixer_callback.vader @@ -21,9 +21,9 @@ Execute(The standardrb callback should return the correct default values): AssertEqual \ { - \ 'read_temporary_file': 1, + \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_standardrb_executable) - \ . ' --fix --force-exclusion %t', + \ . ' --fix --force-exclusion --stdin %s', \ }, \ ale#fixers#standardrb#Fix(bufnr('')) @@ -32,10 +32,10 @@ Execute(The standardrb callback should include configuration files): AssertEqual \ { - \ 'read_temporary_file': 1, + \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_standardrb_executable) \ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.standard.yml')) - \ . ' --fix --force-exclusion %t', + \ . ' --fix --force-exclusion --stdin %s', \ }, \ ale#fixers#standardrb#Fix(bufnr('')) @@ -45,10 +45,10 @@ Execute(The standardrb callback should include custom rubocop options): AssertEqual \ { - \ 'read_temporary_file': 1, + \ 'process_with': 'ale#fixers#rubocop#PostProcess', \ 'command': ale#Escape(g:ale_ruby_standardrb_executable) \ . ' --config ' . ale#Escape(ale#path#Simplify(g:dir . '/ruby_paths/with_config/.standard.yml')) \ . ' --except Lint/Debugger' - \ . ' --fix --force-exclusion %t', + \ . ' --fix --force-exclusion --stdin %s', \ }, \ ale#fixers#standardrb#Fix(bufnr('')) diff --git a/test/fixers/test_xo_fixer_callback.vader b/test/fixers/test_xo_fixer_callback.vader new file mode 100644 index 00000000..a473606e --- /dev/null +++ b/test/fixers/test_xo_fixer_callback.vader @@ -0,0 +1,45 @@ +Before: + call ale#assert#SetUpFixerTest('javascript', 'xo') + runtime autoload/ale/handlers/xo.vim + set filetype=javascript + +After: + call ale#assert#TearDownFixerTest() + +Execute(The xo callback should return the correct default values): + call ale#test#SetFilename('../xo-test-files/monorepo/packages/a/index.js') + + AssertFixer + \ { + \ 'read_temporary_file': 1, + \ 'command': (has('win32') ? 'node.exe ' : '') + \ . ale#Escape(ale#path#Simplify(g:dir . '/../xo-test-files/monorepo/node_modules/xo/cli.js')) + \ . ' --fix %t', + \ } + +Execute(The xo callback should include custom xo options): + let g:ale_javascript_xo_options = '--space' + call ale#test#SetFilename('../xo-test-files/monorepo/packages/a/index.js') + + AssertFixer + \ { + \ 'read_temporary_file': 1, + \ 'command': (has('win32') ? 'node.exe ' : '') + \ . ale#Escape(ale#path#Simplify(g:dir . '/../xo-test-files/monorepo/node_modules/xo/cli.js')) + \ . ' --fix %t' + \ . ' --space', + \ } + +Execute(--stdin should be used when xo is new enough): + let g:ale_javascript_xo_options = '--space' + call ale#test#SetFilename('../xo-test-files/monorepo/packages/a/index.js') + + GivenCommandOutput ['0.30.0'] + AssertFixer + \ { + \ 'command': (has('win32') ? 'node.exe ' : '') + \ . ale#Escape(ale#path#Simplify(g:dir . '/../xo-test-files/monorepo/node_modules/xo/cli.js')) + \ . ' --stdin --stdin-filename %s' + \ . ' --fix' + \ . ' --space', + \ } diff --git a/test/fixers/test_xots_fixer_callback.vader b/test/fixers/test_xots_fixer_callback.vader new file mode 100644 index 00000000..5c7fa1d1 --- /dev/null +++ b/test/fixers/test_xots_fixer_callback.vader @@ -0,0 +1,45 @@ +Before: + call ale#assert#SetUpFixerTest('typescript', 'xo') + runtime autoload/ale/handlers/xo.vim + set filetype=typescript + +After: + call ale#assert#TearDownFixerTest() + +Execute(The xo callback should return the correct default values): + call ale#test#SetFilename('../xo-test-files/monorepo/packages/a/index.ts') + + AssertFixer + \ { + \ 'read_temporary_file': 1, + \ 'command': (has('win32') ? 'node.exe ' : '') + \ . ale#Escape(ale#path#Simplify(g:dir . '/../xo-test-files/monorepo/node_modules/xo/cli.js')) + \ . ' --fix %t', + \ } + +Execute(The xo callback should include custom xo options): + let g:ale_typescript_xo_options = '--space' + call ale#test#SetFilename('../xo-test-files/monorepo/packages/a/index.ts') + + AssertFixer + \ { + \ 'read_temporary_file': 1, + \ 'command': (has('win32') ? 'node.exe ' : '') + \ . ale#Escape(ale#path#Simplify(g:dir . '/../xo-test-files/monorepo/node_modules/xo/cli.js')) + \ . ' --fix %t' + \ . ' --space', + \ } + +Execute(--stdin should be used when xo is new enough): + let g:ale_typescript_xo_options = '--space' + call ale#test#SetFilename('../xo-test-files/monorepo/packages/a/index.ts') + + GivenCommandOutput ['0.30.0'] + AssertFixer + \ { + \ 'command': (has('win32') ? 'node.exe ' : '') + \ . ale#Escape(ale#path#Simplify(g:dir . '/../xo-test-files/monorepo/node_modules/xo/cli.js')) + \ . ' --stdin --stdin-filename %s' + \ . ' --fix' + \ . ' --space', + \ } diff --git a/test/fixers/test_yamlfix_fixer_callback.vader b/test/fixers/test_yamlfix_fixer_callback.vader new file mode 100644 index 00000000..3ffda91e --- /dev/null +++ b/test/fixers/test_yamlfix_fixer_callback.vader @@ -0,0 +1,50 @@ +Before: + Save g:ale_python_yamlfix_executable + Save g:ale_python_yamlfix_options + + " Use an invalid global executable, so we don't match it. + let g:ale_python_yamlfix_executable = 'xxxinvalid' + let g:ale_python_yamlfix_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + silent cd .. + silent cd command_callback + let g:dir = getcwd() + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + +After: + Restore + + unlet! b:bin_dir + + call ale#test#RestoreDirectory() + +Execute(The yamlfix callback should return the correct default values): + AssertEqual + \ 0, + \ ale#fixers#yamlfix#Fix(bufnr('')) + + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.yaml') + AssertEqual + \ { + \ 'command': ale#path#BufferCdString(bufnr('')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yamlfix')) . ' -', + \ }, + \ ale#fixers#yamlfix#Fix(bufnr('')) + +Execute(The yamlfix callback should respect custom options): + let g:ale_yaml_yamlfix_options = '--multi-line=3 --trailing-comma' + + AssertEqual + \ 0, + \ ale#fixers#yamlfix#Fix(bufnr('')) + + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.yaml') + AssertEqual + \ { + \ 'command': ale#path#BufferCdString(bufnr('')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yamlfix')) + \ . ' --multi-line=3 --trailing-comma -', + \ }, + \ ale#fixers#yamlfix#Fix(bufnr('')) diff --git a/test/handler/test_atools_handler.vader b/test/handler/test_atools_handler.vader new file mode 100644 index 00000000..1bb9ca00 --- /dev/null +++ b/test/handler/test_atools_handler.vader @@ -0,0 +1,85 @@ +Before: + runtime autoload/ale/handlers/atools.vim + +After: + call ale#linter#Reset() + +Execute(The atools handler should handle basic errors or warings): + AssertEqual + \ [ + \ { + \ 'lnum': 2, + \ 'text': 'trailing whitespace', + \ 'type': 'E', + \ 'code': 'AL8', + \ }, + \ { + \ 'lnum': 15, + \ 'text': '$pkgname should not be used in the source url', + \ 'type': 'W', + \ 'code': 'AL29', + \ }, + \ ], + \ ale#handlers#atools#Handle(bufnr(''), [ + \ 'IC:[AL8]:APKBUILD:2:trailing whitespace', + \ 'MC:[AL29]:APKBUILD:15:$pkgname should not be used in the source url', + \ ]) + +" Regardless of the severity, if the certainty is [P]ossible and not [C]ertain +" or if regardless of the Certainity the Severity is not [I]mportant or [S]erious +" then it must be a [W]arning +Execute(If we are not Certain or Importantly Serious, be a Warning): + AssertEqual + \ [ + \ { + \ 'lnum': 3, + \ 'text': 'This violation is Serious but Possible false positive, I am a Warning!', + \ 'type': 'W', + \ 'code': 'AL', + \ }, + \ { + \ 'lnum': 4, + \ 'text': 'This violation is Important but Possible false positive, I am a Warning!', + \ 'type': 'W', + \ 'code': 'AL', + \ }, + \ { + \ 'lnum': 5, + \ 'text': 'This violation is Minor, I am a Warning!', + \ 'type': 'W', + \ 'code': 'AL', + \ }, + \ { + \ 'lnum': 6, + \ 'text': 'This violation is Style, I am a Warning!', + \ 'type': 'W', + \ 'code': 'AL', + \ }, + \ ], + \ ale#handlers#atools#Handle(bufnr(''), [ + \ 'SP:[AL]:APKBUILD:3:This violation is Serious but Possible false positive, I am a Warning!', + \ 'IP:[AL]:APKBUILD:4:This violation is Important but Possible false positive, I am a Warning!', + \ 'MC:[AL]:APKBUILD:5:This violation is Minor, I am a Warning!', + \ 'TC:[AL]:APKBUILD:6:This violation is Style, I am a Warning!', + \ ]) + +Execute(We should be error if we are Certain it is Serious or Important): + AssertEqual + \ [ + \ { + \ 'lnum': 7, + \ 'text': 'This is Certainly Serious, I am an Error!', + \ 'type': 'E', + \ 'code': 'AL', + \ }, + \ { + \ 'lnum': 8, + \ 'text': 'This is Certainly Important, I am an Error!', + \ 'type': 'E', + \ 'code': 'AL', + \ }, + \ ], + \ ale#handlers#atools#Handle(bufnr(''), [ + \ 'SC:[AL]:APKBUILD:7:This is Certainly Serious, I am an Error!', + \ 'IC:[AL]:APKBUILD:8:This is Certainly Important, I am an Error!', + \ ]) diff --git a/test/handler/test_dafny_handler.vader b/test/handler/test_dafny_handler.vader index 674f691d..4ca288d2 100644 --- a/test/handler/test_dafny_handler.vader +++ b/test/handler/test_dafny_handler.vader @@ -8,21 +8,29 @@ Execute(The Dafny handler should parse output correctly): AssertEqual \ [ \ { - \ 'bufnr': 0, + \ 'filename': 'File.dfy', \ 'col': 45, \ 'lnum': 123, \ 'text': 'A precondition for this call might not hold.', \ 'type': 'E' \ }, \ { - \ 'bufnr': 0, + \ 'filename': 'File.dfy', \ 'col': 90, \ 'lnum': 678, \ 'text': 'This is the precondition that might not hold.', \ 'type': 'W' - \ } + \ }, + \ { + \ 'filename': 'File.dfy', + \ 'col': 45, + \ 'lnum': 123, + \ 'text': "Verification of 'Impl$$_22_Proof.__default.PutKeepsMapsFull' timed out after 2 seconds", + \ 'type': 'E' + \ }, \ ], \ ale_linters#dafny#dafny#Handle(0, [ \ 'File.dfy(123,45): Error BP5002: A precondition for this call might not hold.', - \ 'File.dfy(678,90): Related location: This is the precondition that might not hold.' + \ 'File.dfy(678,90): Related location: This is the precondition that might not hold.', + \ "File.dfy(123,45): Verification of 'Impl$$_22_Proof.__default.PutKeepsMapsFull' timed out after 2 seconds", \ ]) diff --git a/test/handler/test_erlang_elvis_handler.vader b/test/handler/test_erlang_elvis_handler.vader new file mode 100644 index 00000000..365376c8 --- /dev/null +++ b/test/handler/test_erlang_elvis_handler.vader @@ -0,0 +1,37 @@ +Before: + runtime ale_linters/erlang/elvis.vim + +After: + call ale#linter#Reset() + +Execute(Warning messages should be handled): + AssertEqual + \ [ + \ { + \ 'lnum': 11, + \ 'text': "Replace the 'if' expression on line 11 with a 'case' expression or function clauses.", + \ 'type': 'W', + \ }, + \ { + \ 'lnum': 20, + \ 'text': 'Remove the debug call to io:format/1 on line 20.', + \ 'type': 'W', + \ }, + \ ], + \ ale_linters#erlang#elvis#Handle(bufnr(''), [ + \ "src/foo.erl:11:no_if_expression:Replace the 'if' expression on line 11 with a 'case' expression or function clauses.", + \ 'src/foo.erl:20:no_debug_call:Remove the debug call to io:format/1 on line 20.', + \ ]) + +Execute(Line length message shouldn't contain the line itself): + AssertEqual + \ [ + \ { + \ 'lnum': 24, + \ 'text': 'Line 24 is too long.', + \ 'type': 'W', + \ }, + \ ], + \ ale_linters#erlang#elvis#Handle(bufnr(''), [ + \ 'src/foo.erl:24:line_length:Line 24 is too long: io:format("Look ma, too long!"),.', + \ ]) diff --git a/test/handler/test_hadolint.vader b/test/handler/test_hadolint.vader new file mode 100644 index 00000000..eec71fbc --- /dev/null +++ b/test/handler/test_hadolint.vader @@ -0,0 +1,49 @@ +Before: + runtime ale_linters/dockerfile/hadolint.vim + +After: + call ale#linter#Reset() + +Execute(The hadolint handler should handle an empty string response): + AssertEqual + \ [], + \ ale_linters#dockerfile#hadolint#Handle(bufnr(''), []) + +Execute(The hadolint handler should handle a normal example): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 0, + \ 'type': 'W', + \ 'text': "Always tag the version of an image explicitly", + \ 'detail': "DL3006 ( https://github.com/hadolint/hadolint/wiki/DL3006 )\n\nAlways tag the version of an image explicitly", + \ }, +# \ { +# \ 'lnum': 2, +# \ 'col': 0, +# \ 'type': 'E', +# \ 'text': "MAINTAINER is deprecated", +# \ 'detail': "DL4000 ( https://github.com/hadolint/hadolint/wiki/DL4000 )\n\nMAINTAINER is deprecated", +# \ }, +# \ { +# \ 'lnum': 4, +# \ 'col': 0, +# \ 'type': 'W', +# \ 'text': "Specify version with `yum install -y <package>-<version>`.", +# \ 'detail': "DL3033 ( https://github.com/hadolint/hadolint/wiki/DL3033 )\n\nSpecify version with `yum install -y <package>-<version>`.", +# \ }, +# \ { +# \ 'lnum': 12, +# \ 'col': 0, +# \ 'type': 'W', +# \ 'text': "In POSIX sh, brace expansion is undefined.", +# \ 'detail': "SC2039 ( https://github.com/koalaman/shellcheck/wiki/SC2039 )\n\nIn POSIX sh, brace expansion is undefined.", +# \ }, + \ ], + \ ale_linters#dockerfile#hadolint#Handle(bufnr(''), [ + \ '/dev/stdin:1 DL3006 warning: Always tag the version of an image explicitly', +# \ '/dev/stdin:2 DL4000 error: MAINTAINER is deprecated', +# \ '/dev/stdin:4 DL3033 warning: Specify version with `yum install -y <package>-<version>`.', +# \ '/dev/stdin:12 SC2039 warning: In POSIX sh, brace expansion is undefined.' + \ ]) diff --git a/test/handler/test_ibm_openapi_validator_handler.vader b/test/handler/test_ibm_openapi_validator_handler.vader new file mode 100644 index 00000000..e136d5d2 --- /dev/null +++ b/test/handler/test_ibm_openapi_validator_handler.vader @@ -0,0 +1,49 @@ +Before: + runtime! ale_linters/openapi/ibm_validator.vim + +After: + call ale#linter#Reset() + +Execute(Problems should be parsed correctly for openapi-ibm-validator): + AssertEqual + \ [ + \ { + \ 'lnum': 54, + \ 'col': 0, + \ 'type': 'E', + \ 'text': 'Items with a description must have content in it.', + \ }, + \ { + \ 'lnum': 24, + \ 'col': 0, + \ 'type': 'W', + \ 'text': 'Operations must have a non-empty `operationId`.', + \ }, + \ { + \ 'lnum': 40, + \ 'col': 0, + \ 'type': 'W', + \ 'text': 'operationIds must follow case convention: lower_snake_case', + \ }, + \ ], + \ ale_linters#openapi#ibm_validator#Handle(bufnr(''), [ + \ '', + \ '[Warning] No .validaterc file found. The validator will run in default mode.', + \ 'To configure the validator, create a .validaterc file.', + \ '', + \ 'errors', + \ '', + \ ' Message : Items with a description must have content in it.', + \ ' Path : paths./settings.patch.description', + \ ' Line : 54', + \ '', + \ 'warnings', + \ '', + \ ' Message : Operations must have a non-empty `operationId`.', + \ ' Path : paths./stats.get.operationId', + \ ' Line : 24', + \ '', + \ ' Message : operationIds must follow case convention: lower_snake_case', + \ ' Path : paths./settings.get.operationId', + \ ' Line : 40' + \ ]) diff --git a/test/handler/test_inko_handler.vader b/test/handler/test_inko_handler.vader new file mode 100644 index 00000000..6621d2d6 --- /dev/null +++ b/test/handler/test_inko_handler.vader @@ -0,0 +1,54 @@ +Before: + runtime ale_linters/inko/inko.vim + +After: + call ale#linter#Reset() + +Execute(The inko handler should parse errors correctly): + AssertEqual + \ [ + \ { + \ 'filename': ale#path#Simplify('/tmp/foo.inko'), + \ 'lnum': 4, + \ 'col': 5, + \ 'text': 'this is an error', + \ 'type': 'E', + \ } + \ ], + \ ale#handlers#inko#Handle(bufnr(''), [ + \ '[', + \ ' {', + \ ' "file": "/tmp/foo.inko",', + \ ' "line": 4,', + \ ' "column": 5,', + \ ' "message": "this is an error",', + \ ' "level": "error"', + \ ' }', + \ ']' + \ ]) + +Execute(The inko handler should parse warnings correctly): + AssertEqual + \ [ + \ { + \ 'filename': ale#path#Simplify('/tmp/foo.inko'), + \ 'lnum': 4, + \ 'col': 5, + \ 'text': 'this is a warning', + \ 'type': 'W', + \ } + \ ], + \ ale#handlers#inko#Handle(bufnr(''), [ + \ '[', + \ ' {', + \ ' "file": "/tmp/foo.inko",', + \ ' "line": 4,', + \ ' "column": 5,', + \ ' "message": "this is a warning",', + \ ' "level": "warning"', + \ ' }', + \ ']' + \ ]) + +Execute(The inko handler should handle empty output): + AssertEqual [], ale#handlers#inko#Handle(bufnr(''), []) diff --git a/test/handler/test_nix_handler.vader b/test/handler/test_nix_handler.vader index 398e1ac8..87e8b68f 100644 --- a/test/handler/test_nix_handler.vader +++ b/test/handler/test_nix_handler.vader @@ -8,6 +8,29 @@ Execute(The nix handler should parse nix-instantiate error messages correctly): AssertEqual \ [ \ { + \ 'lnum': 6, + \ 'col': 3, + \ 'type': 'E', + \ 'text': "syntax error, unexpected ']', expecting ';'", + \ }, + \ { + \ 'lnum': 3, + \ 'col': 5, + \ 'type': 'E', + \ 'text': "undefined variable 'foo'", + \ }, + \ + \ ], + \ ale_linters#nix#nix#Handle(bufnr(''), [ + \ "@nix {\"line\":6,\"column\":3,\"raw_msg\":\"syntax error, unexpected ']', expecting ';'\"}", + \ "@nix {\"line\":3,\"column\":5,\"raw_msg\":\"undefined variable 'foo'\"}", + \ "@nix {\"unrelated\":\"message\"}" + \ ]) + +Execute(The nix handler should parse message from old nix-instantiate correctly): + AssertEqual + \ [ + \ { \ 'lnum': 23, \ 'col': 14, \ 'text': 'error: syntax error, unexpected IN', diff --git a/test/handler/test_phpcs_handler.vader b/test/handler/test_phpcs_handler.vader index 18accece..26d35cb8 100644 --- a/test/handler/test_phpcs_handler.vader +++ b/test/handler/test_phpcs_handler.vader @@ -13,7 +13,16 @@ Execute(phpcs errors should be handled): \ 'type': 'E', \ 'sub_type': 'style', \ 'text': 'Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)', - \ }], + \ }, + \ { + \ 'lnum': 22, + \ 'col': 3, + \ 'type': 'E', + \ 'sub_type': 'style', + \ 'text': 'All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks)', + \ }, + \ ], \ ale_linters#php#phpcs#Handle(bufnr(''), [ \ '/path/to/some-filename.php:18:3: error - Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)', + \ "/path/to/some-filename.php:22:3: error - All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found '\"\n'.", \ ]) diff --git a/test/handler/test_salt_salt_lint.vader b/test/handler/test_salt_salt_lint.vader new file mode 100644 index 00000000..7e234785 --- /dev/null +++ b/test/handler/test_salt_salt_lint.vader @@ -0,0 +1,34 @@ +Before: + runtime ale_linters/salt/salt_lint.vim + +After: + call ale#linter#Reset() + +Execute(The salt handler should parse lines correctly and show error in severity HIGH): + AssertEqual + \ [ + \ { + \ 'lnum': 5, + \ 'code': 207, + \ 'text': 'File modes should always be encapsulated in quotation marks', + \ 'type': 'E' + \ } + \ ], + \ ale_linters#salt#salt_lint#Handle(255, [ + \ '[{"id": "207", "message": "File modes should always be encapsulated in quotation marks", "filename": "test.sls", "linenumber": 5, "line": " - mode: 0755", "severity": "HIGH"}]' + \ ]) + + +Execute(The salt handler should parse lines correctly and show error in severity not HIGH): + AssertEqual + \ [ + \ { + \ 'lnum': 27, + \ 'code': 204, + \ 'text': 'Lines should be no longer that 160 chars', + \ 'type': 'W' + \ } + \ ], + \ ale_linters#salt#salt_lint#Handle(255, [ + \ '[{"id": "204", "message": "Lines should be no longer that 160 chars", "filename": "test2.sls", "linenumber": 27, "line": "this line is definitely longer than 160 chars, this line is definitely longer than 160 chars, this line is definitely longer than 160 chars", "severity": "VERY_LOW"}]' + \ ]) diff --git a/test/handler/test_spectral_handler.vader b/test/handler/test_spectral_handler.vader new file mode 100644 index 00000000..89a3ff1b --- /dev/null +++ b/test/handler/test_spectral_handler.vader @@ -0,0 +1,52 @@ +Before: + runtime ale_linters/yaml/spectral.vim + +After: + call ale#linter#Reset() + +Execute(spectral handler should parse lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 1, + \ 'code': 'oas3-api-servers', + \ 'text': 'OpenAPI `servers` must be present and non-empty array.', + \ 'type': 'W' + \ }, + \ { + \ 'lnum': 1, + \ 'col': 1, + \ 'code': 'oas3-schema', + \ 'text': 'Object should have required property `paths`.', + \ 'type': 'E' + \ }, + \ { + \ 'lnum': 1, + \ 'col': 1, + \ 'code': 'openapi-tags', + \ 'text': 'OpenAPI object should have non-empty `tags` array.', + \ 'type': 'W' + \ }, + \ { + \ 'lnum': 3, + \ 'col': 6, + \ 'code': 'info-contact', + \ 'text': 'Info object should contain `contact` object.', + \ 'type': 'W' + \ }, + \ { + \ 'lnum': 3, + \ 'col': 6, + \ 'code': 'oas3-schema', + \ 'text': '`info` property should have required property `version`.', + \ 'type': 'E' + \ }, + \ ], + \ ale#handlers#spectral#HandleSpectralOutput(bufnr(''), [ + \ 'openapi.yml:1:1 warning oas3-api-servers "OpenAPI `servers` must be present and non-empty array."', + \ 'openapi.yml:1:1 error oas3-schema "Object should have required property `paths`."', + \ 'openapi.yml:1:1 warning openapi-tags "OpenAPI object should have non-empty `tags` array."', + \ 'openapi.yml:3:6 warning info-contact "Info object should contain `contact` object."', + \ 'openapi.yml:3:6 error oas3-schema "`info` property should have required property `version`."', + \ ]) diff --git a/test/handler/test_swipl_handler.vader b/test/handler/test_swipl_handler.vader index 9e425cf6..81b8b9e5 100644 --- a/test/handler/test_swipl_handler.vader +++ b/test/handler/test_swipl_handler.vader @@ -35,6 +35,22 @@ Execute (The swipl handler should handle a warning / error of two lines): \ ' Singleton variables: [M]', \ ]) +Execute (The swipl handler should handle a warning / error of two lines in the new format): + call ale#test#SetFilename('test.pl') + AssertEqual + \ [ + \ { + \ 'lnum': 9, + \ 'col': 0, + \ 'text': 'Singleton variables: [M]', + \ 'type': 'W', + \ }, + \ ], + \ ale_linters#prolog#swipl#Handle(bufnr(''), [ + \ 'Warning: /path/to/test.pl:9:', + \ 'Warning: Singleton variables: [M]', + \ ]) + Execute (The swipl handler should join three or more lines with '. '): call ale#test#SetFilename('test.pl') AssertEqual @@ -79,6 +95,50 @@ Execute (The swipl handler should ignore warnings / errors 'No permission to cal \ ' vimscript: (multifile A)', \ ]) +Execute (The swipl handler should join three or more lines with '. ' on latest swipl): + call ale#test#SetFilename('test.pl') + AssertEqual + \ [ + \ { + \ 'lnum': 10, + \ 'col': 0, + \ 'text': 'Clauses of fib/2 are not together in the source-file. Earlier definition at /path/to/test.pl:7. Current predicate: f/0. Use :- discontiguous fib/2. to suppress this message', + \ 'type': 'W', + \ }, + \ ], + \ ale_linters#prolog#swipl#Handle(bufnr(''), [ + \ 'Warning: /path/to/test.pl:10:', + \ 'Warning: Clauses of fib/2 are not together in the source-file', + \ 'Warning: Earlier definition at /path/to/test.pl:7', + \ 'Warning: Current predicate: f/0', + \ 'Warning: Use :- discontiguous fib/2. to suppress this message', + \ ]) + +Execute (The swipl handler should ignore warnings / errors 'No permission to call sandboxed with latest swpl...'): + call ale#test#SetFilename('test.pl') + AssertEqual + \ [], + \ ale_linters#prolog#swipl#Handle(bufnr(''), [ + \ 'ERROR: /path/to/test.pl:11:', + \ 'ERROR: No permission to call sandboxed `''$set_predicate_attribute''(_G3416:_G3417,_G3413,_G3414)''', + \ 'ERROR: Reachable from:', + \ 'ERROR: system:''$set_pattr''(A,B,C,D)', + \ 'ERROR: system:''$set_pattr''(vimscript:A,B,C)', + \ 'ERROR: vimscript: (multifile A)', + \ 'ERROR: /path/to/test.pl:12:', + \ 'ERROR: No permission to call sandboxed `''$set_predicate_attribute''(_G205:_G206,_G202,_G203)''', + \ 'ERROR: Reachable from:', + \ 'ERROR: system:''$set_pattr''(A,B,C,D)', + \ 'ERROR: system:''$set_pattr''(vimscript:A,B,C)', + \ 'ERROR: vimscript: (multifile A)', + \ 'ERROR: /path/to/test.pl:13:', + \ 'ERROR: No permission to call sandboxed `''$set_predicate_attribute''(_G1808:_G1809,_G1805,_G1806)''', + \ 'ERROR: Reachable from:', + \ 'ERROR: system:''$set_pattr''(A,B,C,D)', + \ 'ERROR: system:''$set_pattr''(vimscript:A,B,C)', + \ 'ERROR: vimscript: (multifile A)', + \ ]) + Execute (The swipl handler should handle a warning / error with no line number): call ale#test#SetFilename('test.pl') AssertEqual diff --git a/test/handler/test_terraform_handler.vader b/test/handler/test_terraform_handler.vader index 976ce12a..64e2e578 100644 --- a/test/handler/test_terraform_handler.vader +++ b/test/handler/test_terraform_handler.vader @@ -1,34 +1,61 @@ Before: " Load the file which defines the linter. runtime ale_linters/terraform/terraform.vim + call ale#test#SetDirectory('/testplugin/test/terraform_files') + call ale#test#SetFilename('providers.tf') After: " Unload all linters again. call ale#linter#Reset() + call ale#test#RestoreDirectory() Execute(The output should be correct): AssertEqual \ [ \ { - \ 'lnum': 1, - \ 'col': 20, - \ 'type': 'E', - \ 'text': 'illegal char', + \ 'lnum': 17, + \ 'col': 13, + \ 'filename': ale#path#Simplify(g:dir . '/providers.tf'), + \ 'type': 'W', + \ 'text': 'Terraform 0.13 and earlier allowed provider version', \ }, \ { - \ 'lnum': 2, - \ 'col': 14, + \ 'lnum': 0, + \ 'col': 0, + \ 'filename': ale#path#Simplify(g:dir . '/providers.tf'), \ 'type': 'E', - \ 'text': 'literal not terminated', - \ }, - \ { - \ 'lnum': 1, - \ 'type': 'E', - \ 'text': 'object expected closing RBRACE got: EOF', - \ }, + \ 'text': 'Plugin reinitialization required. Please run "terraform"', + \ } \ ], \ ale_linters#terraform#terraform#Handle(bufnr(''), [ - \ 'Error running fmt: In <standard input>: At 1:20: illegal char', - \ 'Error running fmt: In <standard input>: At 2:14: literal not terminated', - \ 'Error running fmt: In <standard input>: object expected closing RBRACE got: EOF', + \ '{', + \ '"valid": false,', + \ '"error_count": 1,', + \ '"warning_count": 1,', + \ '"diagnostics": [', + \ ' {', + \ ' "severity": "warning",', + \ ' "summary": "Version constraints inside provider configuration blocks are deprecated",', + \ ' "detail": "Terraform 0.13 and earlier allowed provider version",', + \ ' "range": {', + \ ' "filename": "providers.tf",', + \ ' "start": {', + \ ' "line": 17,', + \ ' "column": 13,', + \ ' "byte": 669', + \ ' },', + \ ' "end": {', + \ ' "line": 17,', + \ ' "column": 24,', + \ ' "byte": 680', + \ ' }', + \ ' }', + \ ' },', + \ ' {', + \ ' "severity": "error",', + \ ' "summary": "Could not load plugin",', + \ ' "detail": "Plugin reinitialization required. Please run \"terraform\""', + \ ' }', + \ ' ]', + \ '}', \ ]) diff --git a/test/handler/test_tlint_handler.vader b/test/handler/test_tlint_handler.vader new file mode 100644 index 00000000..e146346c --- /dev/null +++ b/test/handler/test_tlint_handler.vader @@ -0,0 +1,34 @@ +Before: + runtime ale_linters/php/tlint.vim + +After: + call ale#linter#Reset() + +Execute(The tlint handler should calculate line numbers): + AssertEqual + \ [ + \ { + \ 'lnum': '5', + \ 'col': 0, + \ 'sub_type': + \ 'style', + \ 'type': 'W', + \ 'text': ['! There should be no unused imports.', 'There should be no unused imports.', '', '', '', '', '', '', '', ''] + \ }, + \ { + \ 'lnum': '15', + \ 'col': 0, + \ 'sub_type': + \ 'style', + \ 'type': 'W', + \ 'text': ['! There should be no method visibility in test methods.', 'There should be no method visibility in test methods.', '', '', '', '', '', '', '', ''] + \ }, + \ ], + \ ale_linters#php#tlint#Handle(347, [ + \ "Lints for /Users/jose/Code/Tighten/tester/tests/Unit/ExampleTest.php", + \ "============", + \ "! There should be no unused imports.", + \ "5 : `use Illuminate\Foundation\Testing\RefreshDatabase;`", + \ "! There should be no method visibility in test methods.", + \ "15 : ` public function testBasicTest()`", + \ ]) diff --git a/test/handler/test_vala_lint_handler.vader b/test/handler/test_vala_lint_handler.vader new file mode 100644 index 00000000..b8a4fbfa --- /dev/null +++ b/test/handler/test_vala_lint_handler.vader @@ -0,0 +1,54 @@ +Before: + runtime ale_linters/vala/vala_lint.vim + +After: + call ale#linter#Reset() + +Execute(The Vala-Lint handler should parse lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 18, + \ 'col': 18, + \ 'text': 'Expected space before paren', + \ 'code': 'space-before-paren', + \ 'type': 'E', + \ }, + \ { + \ 'lnum': 64, + \ 'col': 37, + \ 'text': 'Expected space before paren', + \ 'code': 'space-before-paren', + \ 'type': 'W', + \ }, + \ { + \ 'lnum': 73, + \ 'col': 37, + \ 'text': 'Expected space before paren', + \ 'code': 'space-before-paren', + \ 'type': 'E', + \ }, + \ ], + \ ale_linters#vala#vala_lint#Handle(bufnr(''), [ + \ 'Application.vala', + \ ' 18.18 error Expected space before paren space-before-paren', + \ ' 64.37 warn Expected space before paren space-before-paren', + \ ' 73.37 error Expected space before paren space-before-paren', + \ ]) + +Execute(The Vala-Lint handler should ignore unknown error types): + AssertEqual + \ [ + \ { + \ 'lnum': 73, + \ 'col': 37, + \ 'text': 'Expected space before paren', + \ 'code': 'space-before-paren', + \ 'type': 'E', + \ }, + \ ], + \ ale_linters#vala#vala_lint#Handle(bufnr(''), [ + \ 'Application.vala', + \ ' 18.18 test Expected space before paren space-before-paren', + \ ' 73.37 error Expected space before paren space-before-paren', + \ ]) diff --git a/test/handler/test_yamllint_handler.vader b/test/handler/test_yamllint_handler.vader index 1aa0b9f5..dd51119c 100644 --- a/test/handler/test_yamllint_handler.vader +++ b/test/handler/test_yamllint_handler.vader @@ -3,7 +3,7 @@ Before: let g:ale_warn_about_trailing_whitespace = 1 - runtime! ale_linters/yaml/yamllint.vim + runtime! ale/handlers/yamllint.vim After: Restore @@ -29,7 +29,7 @@ Execute(Problems should be parsed correctly for yamllint): \ 'text': 'syntax error: expected the node content, but found ''<stream end>''', \ }, \ ], - \ ale_linters#yaml#yamllint#Handle(bufnr(''), [ + \ ale#handlers#yamllint#Handle(bufnr(''), [ \ 'something.yaml:1:1: [warning] missing document start "---" (document-start)', \ 'something.yml:2:1: [error] syntax error: expected the node content, but found ''<stream end>''', \ ]) @@ -45,7 +45,7 @@ Execute(The yamllint handler should respect ale_warn_about_trailing_whitespace): \ 'code': 'trailing-spaces', \ }, \ ], - \ ale_linters#yaml#yamllint#Handle(bufnr(''), [ + \ ale#handlers#yamllint#Handle(bufnr(''), [ \ 'something.yml:5:18: [error] trailing spaces (trailing-spaces)', \ ]) @@ -54,6 +54,6 @@ Execute(The yamllint handler should respect ale_warn_about_trailing_whitespace): AssertEqual \ [ \ ], - \ ale_linters#yaml#yamllint#Handle(bufnr(''), [ + \ ale#handlers#yamllint#Handle(bufnr(''), [ \ 'something.yml:5:18: [error] trailing spaces (trailing-spaces)', \ ]) diff --git a/test/lsp/test_other_initialize_message_handling.vader b/test/lsp/test_other_initialize_message_handling.vader index b6ef852a..f3b53843 100644 --- a/test/lsp/test_other_initialize_message_handling.vader +++ b/test/lsp/test_other_initialize_message_handling.vader @@ -23,6 +23,7 @@ Before: \ 'completion_trigger_characters': [], \ 'definition': 0, \ 'symbol_search': 0, + \ 'code_actions': 0, \ }, \} @@ -102,6 +103,7 @@ Execute(Capabilities should bet set up correctly): \ 'definition': 1, \ 'symbol_search': 1, \ 'rename': 1, + \ 'code_actions': 1, \ }, \ b:conn.capabilities AssertEqual [[1, 'initialized', {}]], g:message_list @@ -125,7 +127,7 @@ Execute(Disabled capabilities should be recognised correctly): \ 'referencesProvider': v:false, \ 'textDocumentSync': 2, \ 'documentFormattingProvider': v:true, - \ 'codeActionProvider': v:true, + \ 'codeActionProvider': v:false, \ 'signatureHelpProvider': { \ 'triggerCharacters': ['(', ','], \ }, @@ -146,6 +148,7 @@ Execute(Disabled capabilities should be recognised correctly): \ 'definition': 0, \ 'symbol_search': 0, \ 'rename': 0, + \ 'code_actions': 0, \ }, \ b:conn.capabilities AssertEqual [[1, 'initialized', {}]], g:message_list @@ -197,6 +200,7 @@ Execute(Capabilities should be enabled when send as Dictionaries): \ 'typeDefinition': 1, \ 'symbol_search': 1, \ 'rename': 1, + \ 'code_actions': 1, \ }, \ b:conn.capabilities AssertEqual [[1, 'initialized', {}]], g:message_list diff --git a/test/lua_files/testfile.lua b/test/lua_files/testfile.lua new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/lua_files/testfile.lua diff --git a/test/nix/test.nix b/test/nix/test.nix new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/nix/test.nix diff --git a/test/nix/test_rnix_lsp.vader b/test/nix/test_rnix_lsp.vader new file mode 100644 index 00000000..bf62616b --- /dev/null +++ b/test/nix/test_rnix_lsp.vader @@ -0,0 +1,14 @@ +" Author: jD91mZM2 <me@krake.one> +" Description: Tests for rnix-lsp language client + +Before: + call ale#assert#SetUpLinterTest('nix', 'rnix_lsp') + +After: + call ale#assert#TearDownLinterTest() + +Execute(should start rnix-lsp): + call ale#test#SetFilename('./test.nix') + AssertLSPLanguage 'nix' + AssertLSPOptions {} + AssertLSPProject ale#path#Simplify('.') diff --git a/test/script/check-duplicate-tags b/test/script/check-duplicate-tags new file mode 100755 index 00000000..ec1de788 --- /dev/null +++ b/test/script/check-duplicate-tags @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -e + +grep --exclude=tags -roh '\*.*\*$' doc | sort | uniq -d diff --git a/test/script/check-supported-tools-tables b/test/script/check-supported-tools-tables index f4305707..d238e77f 100755 --- a/test/script/check-supported-tools-tables +++ b/test/script/check-supported-tools-tables @@ -15,14 +15,13 @@ while read -r; do if [[ "$REPLY" =~ ^! ]]; then language="${REPLY/!/}" else - # shellcheck disable=SC2001 echo "$language - $REPLY" fi done < <( grep '^\*\|^ *\*' doc/ale-supported-languages-and-tools.txt \ | sed -e '1,2d' \ | sed 's/^\* */!/' \ - | sed 's/^ *\* *\|!!\|\^\|(.*)\|`//g' \ + | sed -E 's/^ *\* *|!!|\^|\(.*\)|`//g' \ | sed 's/ *$//' ) > "$doc_file" @@ -30,13 +29,12 @@ while read -r; do if [[ "$REPLY" =~ ^! ]]; then language="${REPLY/!/}" else - # shellcheck disable=SC2001 echo "$language - $REPLY" fi done < <( grep '^\*\|^ *\*' supported-tools.md \ | sed 's/^\* */!/' \ - | sed 's/^ *\* *\|:floppy_disk:\|:warning:\|(.*)\|\[\|\].*\|-n flag//g' \ + | sed -E 's/^ *\* *|:floppy_disk:|:warning:|\(.*\)|\[|\].*|-n flag//g' \ | sed 's/ *$//' ) > "$readme_file" diff --git a/test/script/check-tag-alignment b/test/script/check-tag-alignment new file mode 100755 index 00000000..d41db160 --- /dev/null +++ b/test/script/check-tag-alignment @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +exit_code=0 + +# Documentation tags need to be aligned to the right margin, so look for +# tags which aren't at the right margin. +grep ' \*[^*]\+\*$' doc/ -r \ + | awk '{ sep = index($0, ":"); if (length(substr($0, sep + 1 )) < 79) { print } }' \ + | grep . && exit_code=1 + +exit $exit_code diff --git a/test/script/check-tag-references b/test/script/check-tag-references new file mode 100755 index 00000000..45e741fb --- /dev/null +++ b/test/script/check-tag-references @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -e + +exit_code=0 +tag_regex='[gb]\?:\?\(ale\|ALE\)[a-zA-Z_\-]\+' + +tags="$(mktemp -t tags.XXXXXXXX)" +refs="$(mktemp -t refs.XXXXXXXX)" +# Grep for tags and references, and complain if we find a reference without +# a tag for the reference. Only our tags will be included. +grep --exclude=tags -roh "\\*$tag_regex\\*" doc | sed 's/*//g' | sort -u > "$tags" +grep --exclude=tags -roh "|$tag_regex|" doc | sed 's/|//g' | sort -u > "$refs" + +exit_code=0 + +if ! [[ $(comm -23 $refs $tags | wc -l) -eq 0 ]]; then + exit_code=1 +fi + +rm "$tags" +rm "$refs" diff --git a/test/script/check-toc b/test/script/check-toc index 87a61262..f3f8a9ea 100755 --- a/test/script/check-toc +++ b/test/script/check-toc @@ -35,7 +35,7 @@ sed -n "$toc_start_line,$toc_end_line"p doc/ale.txt \ > "$toc_file" # Get all of the doc files in a natural sorted order. -doc_files="$(/usr/bin/env ls -1v doc | grep ^ale- | sed 's/^/doc\//' | paste -sd ' ' -)" +doc_files="$(/usr/bin/env ls -1v doc | grep '^ale-' | sed 's/^/doc\//' | paste -sd ' ' -)" # shellcheck disable=SC2086 grep -h '\*ale-.*-options\|^[a-z].*\*ale-.*\*$' $doc_files \ diff --git a/test/script/custom-checks b/test/script/custom-checks index ca9069e4..83afb28c 100755 --- a/test/script/custom-checks +++ b/test/script/custom-checks @@ -13,7 +13,7 @@ echo 'Custom warnings/errors follow:' echo set -o pipefail -docker run -a stdout "${docker_flags[@]}" test/script/custom-linting-rules . || exit_code=$? +docker run "${docker_flags[@]}" test/script/custom-linting-rules . || exit_code=$? set +o pipefail echo @@ -23,7 +23,10 @@ echo '========================================' echo 'Duplicate tags follow:' echo -grep --exclude=tags -roh '\*.*\*$' doc | sort | uniq -d || exit_code=$? +set -o pipefail +docker run "${docker_flags[@]}" test/script/check-duplicate-tags . || exit_code=$? +set +o pipefail +echo echo '========================================' echo 'Checking for invalid tag references' @@ -31,14 +34,9 @@ echo '========================================' echo 'Invalid tag references tags follow:' echo -tag_regex='[gb]\?:\?\(ale\|ALE\)[a-zA-Z_\-]\+' - -# Grep for tags and references, and complain if we find a reference without -# a tag for the reference. Only our tags will be included. -diff -u \ - <(grep --exclude=tags -roh "\\*$tag_regex\\*" doc | sort -u | sed 's/*//g') \ - <(grep --exclude=tags -roh "|$tag_regex|" doc | sort -u | sed 's/|//g') \ - | grep '^+[^+]' && exit_code=1 +set -o pipefail +docker run "${docker_flags[@]}" test/script/check-tag-references || exit_code=$? +set +o pipefail echo '========================================' echo 'diff supported-tools.md and doc/ale-supported-languages-and-tools.txt tables' @@ -46,7 +44,9 @@ echo '========================================' echo 'Differences follow:' echo -test/script/check-supported-tools-tables || exit_code=$? +set -o pipefail +docker run "${docker_flags[@]}" test/script/check-supported-tools-tables || exit_code=$? +set +o pipefail echo '========================================' echo 'Look for badly aligned doc tags' @@ -54,18 +54,18 @@ echo '========================================' echo 'Badly aligned tags follow:' echo -# Documentation tags need to be aligned to the right margin, so look for -# tags which aren't at the right margin. -grep ' \*[^*]\+\*$' doc/ -r \ - | awk '{ sep = index($0, ":"); if (length(substr($0, sep + 1 )) < 79) { print } }' \ - | grep . && exit_code=1 +set -o pipefail +docker run "${docker_flags[@]}" test/script/check-tag-alignment || exit_code=$? +set +o pipefail echo '========================================' echo 'Look for table of contents issues' echo '========================================' echo -test/script/check-toc || exit_code=$? +set -o pipefail +docker run "${docker_flags[@]}" test/script/check-toc || exit_code=$? +set +o pipefail echo '========================================' echo 'Check Python code' diff --git a/test/script/custom-linting-rules b/test/script/custom-linting-rules index 981a9459..69ab3cc8 100755 --- a/test/script/custom-linting-rules +++ b/test/script/custom-linting-rules @@ -53,17 +53,29 @@ check_errors() { regex="$1" message="$2" include_arg='' + exclude_arg='' if [ $# -gt 2 ]; then include_arg="--include $3" fi + if [ $# -gt 3 ]; then + shift + shift + shift + + while (( "$#" )); do + exclude_arg="$exclude_arg --exclude $1" + shift + done + fi + for directory in "${directories[@]}"; do # shellcheck disable=SC2086 while read -r; do RETURN_CODE=1 echo "$REPLY $message" - done < <(grep -H -n "$regex" $include_arg "$directory"/**/*.vim \ + done < <(grep -H -n "$regex" $include_arg $exclude_arg "$directory"/**/*.vim \ | grep -v 'no-custom-checks' \ | grep -o '^[^:]\+:[0-9]\+' \ | sed 's:^\./::') @@ -92,7 +104,7 @@ if (( FIX_ERRORS )); then done fi -# The arguments are: regex, explanation, [filename_filter] +# The arguments are: regex, explanation, [filename_filter], [list, of, exclusions] check_errors \ '^function.*) *$' \ 'Function without abort keyword (See :help except-compat)' @@ -114,7 +126,10 @@ check_errors '==?' "Use 'is?' instead of '==?'. 0 ==? 'foobar' is true" check_errors '!=#' "Use 'isnot#' instead of '!=#'. 0 !=# 'foobar' is false" check_errors '!=?' "Use 'isnot?' instead of '!=?'. 0 !=? 'foobar' is false" check_errors '^ *:\?echo' "Stray echo line. Use \`execute echo\` if you want to echo something" -check_errors $'name.:.*\'[a-z_]*[^a-z_0-9][a-z_0-9]*\',$' 'Use snake_case names for linters' '*/ale_linters/*' +# Exclusions for grandfathered-in exceptions +exclusions="clojure/clj_kondo.vim elixir/elixir_ls.vim go/golangci_lint.vim swift/swiftformat.vim" +# shellcheck disable=SC2086 +check_errors $'name.:.*\'[a-z_]*[^a-z_0-9][a-z_0-9]*\',$' 'Use snake_case names for linters' '*/ale_linters/*' $exclusions # Checks for improving type checks. check_errors $'\\(==.\\?\\|is\\) type([\'"]\+)' "Use 'is v:t_string' instead" check_errors '\(==.\?\|is\) type([0-9]\+)' "Use 'is v:t_number' instead" @@ -128,8 +143,8 @@ check_errors '\(!=.\?\|isnot\) type({})' "Use 'isnot v:t_dict' instead" check_errors '\(!=.\?\|isnot\) type(function([^)]\+))' "Use 'isnot v:t_func' instead" # Run a Python script to find lines that require padding around them. For -# users without Python installed, we'll skip these checks. Travis CI will run -# the script. +# users without Python installed, we'll skip these checks. GitHub Actions will +# run the script. if command -v python > /dev/null; then if ! test/script/block-padding-checker "$directory"/**/*.vim; then RETURN_CODE=1 diff --git a/test/smoke_test.vader b/test/smoke_test.vader index 0b126cc6..58206049 100644 --- a/test/smoke_test.vader +++ b/test/smoke_test.vader @@ -64,9 +64,9 @@ Execute(Linters should run with the default options): \ 'valid': 1, \ }] - " Try the test a few times over in NeoVim 0.3 or Windows, + " Try the test a few times over in NeoVim 0.3 or Windows or Vim 8.2, " where tests fail randomly. - for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1) + for g:i in range(has('nvim-0.3') || has('win32') || has('patch-8.2.2401') ? 5 : 1) call ale#Queue(0, '') call ale#test#WaitForJobs(2000) @@ -165,9 +165,9 @@ Execute(Previous errors should be removed when linters change): \ 'valid': 1, \}] - " Try the test a few times over in NeoVim 0.3 or Windows, + " Try the test a few times over in NeoVim 0.3 or VIm 8.2 or Windows, " where tests fail randomly. - for g:i in range(has('nvim-0.3') || has('win32') ? 5 : 1) + for g:i in range(has('nvim-0.3') || has('win32') || has('patch-8.2.2401') ? 5 : 1) call ale#Queue(0, '') call ale#test#WaitForJobs(2000) diff --git a/test/test_c_flag_parsing.vader b/test/test_c_flag_parsing.vader index 99722b17..8b02f2b9 100644 --- a/test/test_c_flag_parsing.vader +++ b/test/test_c_flag_parsing.vader @@ -482,6 +482,7 @@ Execute(We should include several important flags): \ . ' -idirafter ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incafter')) \ . ' -iframework ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incframework')) \ . ' -include ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/foo bar')) + \ . ' -imacros ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incmacros')) \ . ' -Dmacro="value"' \ . ' -DGoal=9' \ . ' -D macro2' @@ -511,6 +512,8 @@ Execute(We should include several important flags): \ 'incframework', \ '-include', \ '''foo bar''', + \ '-imacros', + \ 'incmacros', \ '-Dmacro="value"', \ '-DGoal=9', \ '-D', @@ -559,6 +562,7 @@ Execute(We should quote the flags we need to quote): \ . ' -idirafter ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incafter')) \ . ' -iframework ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incframework')) \ . ' -include ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/foo bar')) + \ . ' -imacros ' . ale#Escape(ale#path#Simplify(g:dir . '/test_c_projects/makefile_project/incmacros')) \ . ' ' . ale#Escape('-Dmacro="value"') \ . ' -DGoal=9' \ . ' -D macro2' @@ -591,6 +595,8 @@ Execute(We should quote the flags we need to quote): \ 'incframework', \ '-include', \ '''foo bar''', + \ '-imacros', + \ 'incmacros', \ '-Dmacro="value"', \ '-DGoal=9', \ '-D', diff --git a/test/test_code_action.vader b/test/test_code_action.vader index 2e5d1381..7eabb759 100644 --- a/test/test_code_action.vader +++ b/test/test_code_action.vader @@ -3,6 +3,9 @@ Before: let g:ale_enabled = 0 + " Enable fix end-of-line as tests below expect that + set fixeol + runtime autoload/ale/code_action.vim runtime autoload/ale/util.vim @@ -211,6 +214,7 @@ Execute(End of file can be modified): \) AssertEqual g:test.text + [ + \ '', \ 'type A: string', \ 'type B: number', \ '', diff --git a/test/test_code_action_python.vader b/test/test_code_action_python.vader new file mode 100644 index 00000000..fd30633d --- /dev/null +++ b/test/test_code_action_python.vader @@ -0,0 +1,59 @@ +Given python(An example Python file): + def main(): + a = 1 + c = a + 1 + +Execute(): + let g:changes = [ + \ {'end': {'offset': 7, 'line': 1}, 'newText': 'func_qtffgsv', 'start': {'offset': 5, 'line': 1}}, + \ {'end': {'offset': 9, 'line': 1}, 'newText': '', 'start': {'offset': 8, 'line': 1}}, + \ {'end': {'offset': 15, 'line': 3}, 'newText': " return c\n\n\ndef main():\n c = func_qtffgsvi()\n", 'start': {'offset': 15, 'line': 3}} + \] + + call ale#code_action#ApplyChanges(expand('%:p'), g:changes, 0) + +Expect(The changes should be applied correctly): + def func_qtffgsvi(): + a = 1 + c = a + 1 + return c + + + def main(): + c = func_qtffgsvi() + + +Given python(Second python example): + import sys + import exifread + + def main(): + with open(sys.argv[1], 'rb') as f: + exif = exifread.process_file(f) + dt = str(exif['Image DateTime']) + date = dt[:10].replace(':', '-') + +Execute(): + let g:changes = [ + \ {'end': {'offset': 16, 'line': 2}, 'newText': "\n\ndef func_ivlpdpao(f):\n exif = exifread.process_file(f)\n dt = str(exif['Image DateTime'])\n date = dt[:10].replace(':', '-')\n return date\n", 'start': {'offset': 16, 'line': 2}}, + \ {'end': {'offset': 32, 'line': 6}, 'newText': 'date = func', 'start': {'offset': 9, 'line': 6}}, + \ {'end': {'offset': 42, 'line': 8}, 'newText': "ivlpdpao(f)\n", 'start': {'offset': 33, 'line': 6}} + \] + + call ale#code_action#ApplyChanges(expand('%:p'), g:changes, 0) + +Expect(The changes should be applied correctly): + import sys + import exifread + + + def func_ivlpdpao(f): + exif = exifread.process_file(f) + dt = str(exif['Image DateTime']) + date = dt[:10].replace(':', '-') + return date + + + def main(): + with open(sys.argv[1], 'rb') as f: + date = func_ivlpdpao(f) diff --git a/test/test_codefix.vader b/test/test_codefix.vader new file mode 100644 index 00000000..fc5470aa --- /dev/null +++ b/test/test_codefix.vader @@ -0,0 +1,549 @@ +Before: + call ale#test#SetDirectory('/testplugin/test') + call ale#test#SetFilename('dummy.txt') + Save g:ale_buffer_info + + let g:ale_buffer_info = {} + + let g:old_filename = expand('%:p') + let g:Callback = '' + let g:expr_list = [] + let g:message_list = [] + let g:handle_code_action_called = 0 + let g:code_actions = [] + let g:options = {} + let g:capability_checked = '' + let g:conn_id = v:null + let g:InitCallback = v:null + + runtime autoload/ale/lsp_linter.vim + runtime autoload/ale/lsp.vim + runtime autoload/ale/util.vim + runtime autoload/ale/codefix.vim + runtime autoload/ale/code_action.vim + + function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) + + if a:linter.lsp is# 'tsserver' + call ale#lsp#MarkConnectionAsTsserver(g:conn_id) + endif + + let l:details = { + \ 'command': 'foobar', + \ 'buffer': a:buffer, + \ 'connection_id': g:conn_id, + \ 'project_root': '/foo/bar', + \} + + let g:InitCallback = {-> ale#lsp_linter#OnInit(a:linter, l:details, a:Callback)} + endfunction + + function! ale#lsp#HasCapability(conn_id, capability) abort + let g:capability_checked = a:capability + + return 1 + endfunction + + function! ale#lsp#RegisterCallback(conn_id, callback) abort + let g:Callback = a:callback + endfunction + + function! ale#lsp#Send(conn_id, message) abort + call add(g:message_list, a:message) + + return 42 + endfunction + + function! ale#util#Execute(expr) abort + call add(g:expr_list, a:expr) + endfunction + + function! ale#code_action#HandleCodeAction(code_action, options) abort + let g:handle_code_action_called = 1 + Assert !get(a:options, 'should_save') + call add(g:code_actions, a:code_action) + endfunction + + function! ale#util#Input(message, value) abort + return '2' + endfunction + +After: + Restore + + if g:conn_id isnot v:null + call ale#lsp#RemoveConnectionWithID(g:conn_id) + endif + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + + unlet! g:capability_checked + unlet! g:InitCallback + unlet! g:old_filename + unlet! g:conn_id + unlet! g:Callback + unlet! g:message_list + unlet! g:expr_list + unlet! b:ale_linters + unlet! g:options + unlet! g:code_actions + unlet! g:handle_code_action_called + + runtime autoload/ale/lsp_linter.vim + runtime autoload/ale/lsp.vim + runtime autoload/ale/util.vim + runtime autoload/ale/codefix.vim + runtime autoload/ale/code_action.vim + +Execute(Failed codefix responses should be handled correctly): + call ale#codefix#HandleTSServerResponse( + \ 1, + \ {'command': 'getCodeFixes', 'request_seq': 3} + \) + AssertEqual g:handle_code_action_called, 0 + +Given typescript(Some typescript file): + foo + somelongerline () + bazxyzxyzxyz + +Execute(getCodeFixes from tsserver should be handled): + call ale#codefix#SetMap({3: {}}) + call ale#codefix#HandleTSServerResponse(1, { + \ 'command': 'getCodeFixes', + \ 'request_seq': 3, + \ 'success': v:true, + \ 'type': 'response', + \ 'body': [ + \ { + \ 'description': 'Import default "x" from module "./z"', + \ 'fixName': 'import', + \ 'changes': [ + \ { + \ 'fileName': "/foo/bar/file1.ts", + \ 'textChanges': [ + \ { + \ 'end': { + \ 'line': 2, + \ 'offset': 1, + \ }, + \ 'newText': 'import x from "./z";^@', + \ 'start': { + \ 'line': 2, + \ 'offset': 1, + \ } + \ } + \ ] + \ } + \ ] + \ } + \ ] + \}) + + AssertEqual g:handle_code_action_called, 1 + AssertEqual + \ [ + \ { + \ 'description': 'codefix', + \ 'changes': [ + \ { + \ 'fileName': "/foo/bar/file1.ts", + \ 'textChanges': [ + \ { + \ 'end': { + \ 'line': 2, + \ 'offset': 1 + \ }, + \ 'newText': 'import x from "./z";^@', + \ 'start': { + \ 'line': 2, + \ 'offset': 1 + \ } + \ } + \ ] + \ } + \ ] + \ } + \ ], + \ g:code_actions + +Execute(getCodeFixes from tsserver should be handled with user input if there are more than one action): + call ale#codefix#SetMap({3: {}}) + call ale#codefix#HandleTSServerResponse(1, { + \ 'command': 'getCodeFixes', + \ 'request_seq': 3, + \ 'success': v:true, + \ 'type': 'response', + \ 'body': [ + \ { + \ 'description': 'Import default "x" from module "./z"', + \ 'fixName': 'import', + \ 'changes': [ + \ { + \ 'fileName': "/foo/bar/file1.ts", + \ 'textChanges': [ + \ { + \ 'end': { + \ 'line': 2, + \ 'offset': 1, + \ }, + \ 'newText': 'import x from "./z";^@', + \ 'start': { + \ 'line': 2, + \ 'offset': 1, + \ } + \ } + \ ] + \ } + \ ] + \ }, + \ { + \ 'description': 'Import default "x" from module "./y"', + \ 'fixName': 'import', + \ 'changes': [ + \ { + \ 'fileName': "/foo/bar/file1.ts", + \ 'textChanges': [ + \ { + \ 'end': { + \ 'line': 2, + \ 'offset': 1, + \ }, + \ 'newText': 'import x from "./y";^@', + \ 'start': { + \ 'line': 2, + \ 'offset': 1, + \ } + \ } + \ ] + \ } + \ ] + \ } + \ ] + \}) + + AssertEqual g:handle_code_action_called, 1 + AssertEqual + \ [ + \ { + \ 'description': 'codefix', + \ 'changes': [ + \ { + \ 'fileName': "/foo/bar/file1.ts", + \ 'textChanges': [ + \ { + \ 'end': { + \ 'line': 2, + \ 'offset': 1 + \ }, + \ 'newText': 'import x from "./y";^@', + \ 'start': { + \ 'line': 2, + \ 'offset': 1 + \ } + \ } + \ ] + \ } + \ ] + \ } + \ ], + \ g:code_actions + +Execute(Prints a tsserver error message when getCodeFixes unsuccessful): + call ale#codefix#SetMap({3: {}}) + call ale#codefix#HandleTSServerResponse(1, { + \ 'command': 'getCodeFixes', + \ 'request_seq': 3, + \ 'success': v:false, + \ 'message': 'something is wrong', + \}) + + AssertEqual g:handle_code_action_called, 0 + AssertEqual ['echom ''Error while getting code fixes. Reason: something is wrong'''], g:expr_list + +Execute(Does nothing when where are no code fixes): + call ale#codefix#SetMap({3: {}}) + call ale#codefix#HandleTSServerResponse(1, { + \ 'command': 'getCodeFixes', + \ 'request_seq': 3, + \ 'success': v:true, + \ 'body': [] + \}) + + AssertEqual g:handle_code_action_called, 0 + AssertEqual ['echom ''No code fixes available.'''], g:expr_list + +Execute(tsserver codefix requests should be sent): + call ale#linter#Reset() + + runtime ale_linters/typescript/tsserver.vim + let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5, 'code': 2304}]}} + call setpos('.', [bufnr(''), 2, 16, 0]) + + " ALECodeAction + call ale#codefix#Execute(0) + + " We shouldn't register the callback yet. + AssertEqual '''''', string(g:Callback) + + AssertEqual type(function('type')), type(g:InitCallback) + call g:InitCallback() + + AssertEqual 'code_actions', g:capability_checked + AssertEqual + \ 'function(''ale#codefix#HandleTSServerResponse'')', + \ string(g:Callback) + AssertEqual + \ [ + \ ale#lsp#tsserver_message#Change(bufnr('')), + \ [0, 'ts@getCodeFixes', { + \ 'startLine': 2, + \ 'startOffset': 16, + \ 'endLine': 2, + \ 'endOffset': 17, + \ 'file': expand('%:p'), + \ 'errorCodes': [2304], + \ }] + \ ], + \ g:message_list + +Execute(tsserver codefix requests should be sent only for error with code): + call ale#linter#Reset() + + runtime ale_linters/typescript/tsserver.vim + let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 16}, {'lnum': 2, 'col': 16, 'code': 2304}]}} + call setpos('.', [bufnr(''), 2, 16, 0]) + + " ALECodeAction + call ale#codefix#Execute(0) + + " We shouldn't register the callback yet. + AssertEqual '''''', string(g:Callback) + + AssertEqual type(function('type')), type(g:InitCallback) + call g:InitCallback() + + AssertEqual 'code_actions', g:capability_checked + AssertEqual + \ 'function(''ale#codefix#HandleTSServerResponse'')', + \ string(g:Callback) + AssertEqual + \ [ + \ ale#lsp#tsserver_message#Change(bufnr('')), + \ [0, 'ts@getCodeFixes', { + \ 'startLine': 2, + \ 'startOffset': 16, + \ 'endLine': 2, + \ 'endOffset': 17, + \ 'file': expand('%:p'), + \ 'errorCodes': [2304], + \ }] + \ ], + \ g:message_list + +Execute(getApplicableRefactors from tsserver should be handled): + call ale#codefix#SetMap({3: { + \ 'buffer': expand('%:p'), + \ 'line': 1, + \ 'column': 2, + \ 'end_line': 3, + \ 'end_column': 4, + \ 'connection_id': 0, + \}}) + call ale#codefix#HandleTSServerResponse(1, + \ {'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:true, 'body': [{'actions': [{'description': 'Extract to constant in enclosing scope', 'name': 'constant_scope_0'}], 'description': 'Extract constant', 'name': 'Extract Symbol'}, {'actions': [{'description': 'Extract to function in module scope', 'name': 'function_scope_1'}], 'description': 'Extract function', 'name': 'Extract Symbol'}], 'command': 'getApplicableRefactors'}) + + AssertEqual + \ [ + \ [0, 'ts@getEditsForRefactor', { + \ 'startLine': 1, + \ 'startOffset': 2, + \ 'endLine': 3, + \ 'endOffset': 5, + \ 'file': expand('%:p'), + \ 'refactor': 'Extract Symbol', + \ 'action': 'function_scope_1', + \ }] + \ ], + \ g:message_list + +Execute(getApplicableRefactors should print error on failure): + call ale#codefix#SetMap({3: { + \ 'buffer': expand('%:p'), + \ 'line': 1, + \ 'column': 2, + \ 'end_line': 3, + \ 'end_column': 4, + \ 'connection_id': 0, + \}}) + call ale#codefix#HandleTSServerResponse(1, + \ {'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:false, 'message': 'oops', 'command': 'getApplicableRefactors'}) + + AssertEqual ['echom ''Error while getting applicable refactors. Reason: oops'''], g:expr_list + +Execute(getApplicableRefactors should do nothing if there are no refactors): + call ale#codefix#SetMap({3: { + \ 'buffer': expand('%:p'), + \ 'line': 1, + \ 'column': 2, + \ 'end_line': 3, + \ 'end_column': 4, + \ 'connection_id': 0, + \}}) + call ale#codefix#HandleTSServerResponse(1, + \ {'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:true, 'body': [], 'command': 'getApplicableRefactors'}) + + AssertEqual ['echom ''No applicable refactors available.'''], g:expr_list + +Execute(getEditsForRefactor from tsserver should be handled): + call ale#codefix#SetMap({3: {}}) + call ale#codefix#HandleTSServerResponse(1, + \{'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:true, 'body': {'edits': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 35, 'line': 9}, 'newText': 'newFunction(app);', 'start': {'offset': 3, 'line': 8}}, {'end': {'offset': 4, 'line': 19}, 'newText': '^@function newFunction(app: Router) {^@ app.use(booExpressCsrf());^@ app.use(booExpressRequireHttps);^@}^@', 'start': {'offset': 4, 'line': 19}}]}], 'renameLocation': {'offset': 3, 'line': 8}, 'renameFilename': '/foo/bar/file.ts'}, 'command': 'getEditsForRefactor' } + \) + + AssertEqual g:handle_code_action_called, 1 + AssertEqual + \ [ + \ { + \ 'description': 'editsForRefactor', + \ 'changes': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 35, 'line': 9}, 'newText': 'newFunction(app);', 'start': {'offset': 3, 'line': 8}}, {'end': {'offset': 4, 'line': 19}, 'newText': '^@function newFunction(app: Router) {^@ app.use(booExpressCsrf());^@ app.use(booExpressRequireHttps);^@}^@', 'start': {'offset': 4, 'line': 19}}]}], + \ } + \ ], + \ g:code_actions + +Execute(getEditsForRefactor should print error on failure): + call ale#codefix#SetMap({3: {}}) + call ale#codefix#HandleTSServerResponse(1, + \{'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:false, 'message': 'oops', 'command': 'getEditsForRefactor' } + \) + + AssertEqual ['echom ''Error while getting edits for refactor. Reason: oops'''], g:expr_list + +Execute(Failed LSP responses should be handled correctly): + call ale#codefix#HandleLSPResponse( + \ 1, + \ {'method': 'workspace/applyEdit', 'request_seq': 3} + \) + AssertEqual g:handle_code_action_called, 0 + +Given python(Some python file): + def main(): + a = 1 + b = a + 2 + +Execute("workspace/applyEdit" from LSP should be handled): + call ale#codefix#SetMap({3: {}}) + call ale#codefix#HandleLSPResponse(1, + \ {'id': 0, 'jsonrpc': '2.0', 'method': 'workspace/applyEdit', 'params': {'edit': {'changes': {'file:///foo/bar/file.ts': [{'range': {'end': {'character': 27, 'line': 7}, 'start': {'character': 27, 'line': 7}}, 'newText': ', Config'}, {'range': {'end': {'character': 12, 'line': 96}, 'start': {'character': 2, 'line': 94}}, 'newText': 'await newFunction(redis, imageKey, cover, config);'}, {'range': {'end': {'character': 2, 'line': 99}, 'start': {'character': 2, 'line': 99}}, 'newText': '^@async function newFunction(redis: IRedis, imageKey: string, cover: Buffer, config: Config) {^@ try {^@ await redis.set(imageKey, cover, ''ex'', parseInt(config.coverKeyTTL, 10));^@ }^@ catch { }^@}^@'}]}}}}) + + AssertEqual g:handle_code_action_called, 1 + AssertEqual + \ [{'description': 'applyEdit', 'changes': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 28, 'line': 8}, 'newText': ', Config', 'start': {'offset': 28, 'line': 8}}, {'end': {'offset': 13, 'line': 97}, 'newText': 'await newFunction(redis, imageKey, cover, config);', 'start': {'offset': 3, 'line': 95}}, {'end': {'offset': 3, 'line': 100}, 'newText': '^@async function newFunction(redis: IRedis, imageKey: string, cover: Buffer, config: Config) {^@ try {^@ await redis.set(imageKey, cover, ''ex'', parseInt(config.coverKeyTTL, 10));^@ }^@ catch { }^@}^@', 'start': {'offset': 3, 'line': 100}}]}]}], + \ g:code_actions + +Execute(Code Actions from LSP should be handled with user input if there are more than one action): + call ale#codefix#SetMap({2: {}}) + call ale#codefix#HandleLSPResponse(1, + \ {'id': 2, 'jsonrpc': '2.0', 'result': [{'title': 'fake for testing'}, {'arguments': [{'documentChanges': [{'edits': [{'range': {'end': {'character': 31, 'line': 2}, 'start': {'character': 31, 'line': 2}}, 'newText': ', createVideo'}], 'textDocument': {'uri': 'file:///foo/bar/file.ts', 'version': 1}}]}], 'title': 'Add ''createVideo'' to existing import declaration from "./video"', 'command': '_typescript.applyWorkspaceEdit'}]}) + + AssertEqual g:handle_code_action_called, 1 + AssertEqual + \ [{'description': 'codeaction', 'changes': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 32, 'line': 3}, 'newText': ', createVideo', 'start': {'offset': 32, 'line': 3}}]}]}], + \ g:code_actions + +Execute(Code Actions from LSP should be handled when returned with documentChanges): + call ale#codefix#SetMap({2: {}}) + call ale#codefix#HandleLSPResponse(1, + \ {'id': 2, 'jsonrpc': '2.0', 'result': [{'diagnostics': v:null, 'edit': {'changes': v:null, 'documentChanges': [{'edits': [{'range': {'end': {'character': 4, 'line': 2}, 'start': {'character': 4, 'line': 1}}, 'newText': ''}, {'range': {'end': {'character': 9, 'line': 2}, 'start': {'character': 8, 'line': 2}}, 'newText': '(1)'}], 'textDocument': {'uri': 'file:///foo/bar/test.py', 'version': v:null}}]}, 'kind': 'refactor.inline', 'title': 'Inline variable', 'command': v:null}, {'diagnostics': v:null, 'edit': {'changes': v:null, 'documentChanges': [{'edits': [{'range': {'end': {'character': 0, 'line': 0}, 'start': {'character': 0, 'line': 0}}, 'newText': 'def func_bomdjnxh():^@ a = 1return a^@^@^@'}, {'range': {'end': {'character': 9, 'line': 1}, 'start': {'character': 8, 'line': 1}}, 'newText': 'func_bomdjnxh()^@'}], 'textDocument': {'uri': 'file:///foo/bar/test.py', 'version': v:null}}]}, 'kind': 'refactor.extract', 'title': 'Extract expression into function ''func_bomdjnxh''', 'command': v:null}]}) + + AssertEqual g:handle_code_action_called, 1 + AssertEqual + \ [{'description': 'codeaction', 'changes': [{'fileName': '/foo/bar/test.py', 'textChanges': [{'end': {'offset': 1, 'line': 1}, 'newText': 'def func_bomdjnxh():^@ a = 1return a^@^@^@', 'start': {'offset': 1, 'line': 1}}, {'end': {'offset': 10, 'line': 2}, 'newText': 'func_bomdjnxh()^@', 'start': {'offset': 9, 'line': 2}}]}]}], + \ g:code_actions + +Execute(LSP Code Actions handles command responses): + call ale#codefix#SetMap({3: { + \ 'connection_id': 0, + \}}) + call ale#codefix#HandleLSPResponse(1, + \ {'id': 3, 'jsonrpc': '2.0', 'result': [{'kind': 'refactor', 'title': 'Extract to inner function in function ''getVideo''', 'command': {'arguments': [{'file': '/foo/bar/file.ts', 'endOffset': 0, 'action': 'function_scope_0', 'startOffset': 1, 'startLine': 65, 'refactor': 'Extract Symbol', 'endLine': 68}], 'title': 'Extract to inner function in function ''getVideo''', 'command': '_typescript.applyRefactoring'}}, {'kind': 'refactor', 'title': 'Extract to function in module scope', 'command': {'arguments': [{'file': '/foo/bar/file.ts', 'endOffset': 0, 'action': 'function_scope_1', 'startOffset': 1, 'startLine': 65, 'refactor': 'Extract Symbol', 'endLine': 68}], 'title': 'Extract to function in module scope', 'command': '_typescript.applyRefactoring'}}]}) + + AssertEqual + \ [[0, 'workspace/executeCommand', {'arguments': [{'file': '/foo/bar/file.ts', 'action': 'function_scope_1', 'endOffset': 0, 'refactor': 'Extract Symbol', 'endLine': 68, 'startLine': 65, 'startOffset': 1}], 'command': '_typescript.applyRefactoring'}]], + \ g:message_list + + +Execute(Prints message when LSP code action returns no results): + call ale#codefix#SetMap({3: {}}) + call ale#codefix#HandleLSPResponse(1, + \ {'id': 3, 'jsonrpc': '2.0', 'result': []}) + + AssertEqual g:handle_code_action_called, 0 + AssertEqual ['echom ''No code actions received from server'''], g:expr_list + +Execute(LSP code action requests should be sent): + call ale#linter#Reset() + + runtime ale_linters/python/jedils.vim + let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5, 'end_lnum': 2, 'end_col': 6, 'code': 2304, 'text': 'oops'}]}} + call setpos('.', [bufnr(''), 2, 5, 0]) + + " ALECodeAction + call ale#codefix#Execute(0) + + " We shouldn't register the callback yet. + AssertEqual '''''', string(g:Callback) + + AssertEqual type(function('type')), type(g:InitCallback) + call g:InitCallback() + + AssertEqual 'code_actions', g:capability_checked + AssertEqual + \ 'function(''ale#codefix#HandleLSPResponse'')', + \ string(g:Callback) + AssertEqual + \ [ + \ [0, 'textDocument/codeAction', { + \ 'context': { + \ 'diagnostics': [{'range': {'end': {'character': 6, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}] + \ }, + \ 'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}}, + \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))} + \ }] + \ ], + \ g:message_list[-1:] + +Execute(LSP code action requests should be sent only for error with code): + call ale#linter#Reset() + + runtime ale_linters/python/jedils.vim + let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5, 'end_lnum': 2, 'end_col': 6, 'code': 2304, 'text': 'oops'}]}} + call setpos('.', [bufnr(''), 2, 5, 0]) + + " ALECodeAction + call ale#codefix#Execute(0) + + " We shouldn't register the callback yet. + AssertEqual '''''', string(g:Callback) + + AssertEqual type(function('type')), type(g:InitCallback) + call g:InitCallback() + + AssertEqual 'code_actions', g:capability_checked + AssertEqual + \ 'function(''ale#codefix#HandleLSPResponse'')', + \ string(g:Callback) + AssertEqual + \ [ + \ [0, 'textDocument/codeAction', { + \ 'context': { + \ 'diagnostics': [{'range': {'end': {'character': 6, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}] + \ }, + \ 'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}}, + \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))} + \ }] + \ ], + \ g:message_list[-1:] diff --git a/test/test_deno_executable_detection.vader b/test/test_deno_executable_detection.vader new file mode 100644 index 00000000..edd408b1 --- /dev/null +++ b/test/test_deno_executable_detection.vader @@ -0,0 +1,19 @@ +Before: + runtime autoload/ale/handlers/deno.vim + +After: + unlet! g:ale_deno_executable + + call ale#linter#Reset() + +Execute(Default executable should be detected correctly): + AssertEqual + \ 'deno', + \ ale#handlers#deno#GetExecutable(bufnr('')) + +Execute(User specified executable should override default): + let g:ale_deno_executable = '/path/to/deno-bin' + AssertEqual + \ '/path/to/deno-bin', + \ ale#handlers#deno#GetExecutable(bufnr('')) + diff --git a/test/test_dockerfile_hadolint_linter.vader b/test/test_dockerfile_hadolint_linter.vader index 3edbb2b6..882e1a6c 100644 --- a/test/test_dockerfile_hadolint_linter.vader +++ b/test/test_dockerfile_hadolint_linter.vader @@ -70,14 +70,14 @@ Execute(test warnings from hadolint): AssertEqual \ [{'lnum': 10, 'col': 0, 'type': 'W', 'text': 'Using latest is prone to errors', 'detail': "DL3007 ( https://github.com/hadolint/hadolint/wiki/DL3007 )\n\nUsing latest is prone to errors"}], \ ale_linters#dockerfile#hadolint#Handle(bufnr(''), [ - \ '/dev/stdin:10 DL3007 Using latest is prone to errors', + \ '/dev/stdin:10 DL3007 warning: Using latest is prone to errors', \ ]) Execute(test warnings from shellcheck): AssertEqual \ [{'lnum': 3, 'col': 0, 'type': 'W', 'text': 'bar is referenced but not assigned.', 'detail': "SC2154 ( https://github.com/koalaman/shellcheck/wiki/SC2154 )\n\nbar is referenced but not assigned."}], \ ale_linters#dockerfile#hadolint#Handle(bufnr(''), [ - \ '/dev/stdin:3 SC2154 bar is referenced but not assigned.', + \ '/dev/stdin:3 SC2154 warning: bar is referenced but not assigned.', \ ]) Execute(test errors from dockerfile parser): diff --git a/test/test_filetype_linter_defaults.vader b/test/test_filetype_linter_defaults.vader index e9980536..d4e708ec 100644 --- a/test/test_filetype_linter_defaults.vader +++ b/test/test_filetype_linter_defaults.vader @@ -70,3 +70,10 @@ Execute(The defaults for the verilog filetype should be correct): Execute(Default aliases for React should be defined): AssertEqual ['javascript', 'jsx'], ale#linter#ResolveFiletype('javascriptreact') AssertEqual ['typescript', 'tsx'], ale#linter#ResolveFiletype('typescriptreact') + +Execute(The defaults for the apkbuild filetype should be correct): + AssertEqual ['apkbuild_lint', 'secfixes_check'], GetLinterNames('apkbuild') + + let g:ale_linters_explicit = 1 + + AssertEqual [], GetLinterNames('apkbuild') diff --git a/test/test_floating_preview.vader b/test/test_floating_preview.vader new file mode 100644 index 00000000..43415556 --- /dev/null +++ b/test/test_floating_preview.vader @@ -0,0 +1,92 @@ +Before: + let g:ale_floating_preview = 0 + let g:ale_hover_to_floating_preview = 0 + let g:ale_detail_to_floating_preview = 0 + + runtime autoload/ale/floating_preview.vim + + let g:floated_lines = [] + let g:floating_preview_show_called = 0 + + " Stub out so we can track the call + function! ale#floating_preview#Show(lines, ...) abort + let g:floating_preview_show_called = 1 + let g:floated_lines = a:lines + endfunction + + let g:ale_buffer_info = { + \ bufnr('%'): { + \ 'loclist': [ + \ { + \ 'lnum': 1, + \ 'col': 10, + \ 'bufnr': bufnr('%'), + \ 'vcol': 0, + \ 'linter_name': 'notalinter', + \ 'nr': -1, + \ 'type': 'E', + \ 'code': 'semi', + \ 'text': "Missing semicolon.\r", + \ 'detail': "Every statement should end with a semicolon\nsecond line", + \ }, + \ ], + \ } + \} + + call ale#linter#Reset() + call ale#linter#PreventLoading('javascript') + +After: + Restore + + let g:ale_floating_preview = 0 + let g:ale_hover_to_floating_preview = 0 + let g:ale_detail_to_floating_preview = 0 + + call cursor(1, 1) + + let g:ale_buffer_info = {} + + " Close the preview window if it's open. + if &filetype is# 'ale-preview' + noautocmd :q! + endif + + call ale#linter#Reset() + + +Given javascript(A file with warnings/errors): + var x = 3 + 12345678 + var x = 5*2 + parseInt("10"); + // comment + +Execute(Floating preview is used with ALEDetail when g:ale_floating_preview set): + let g:ale_floating_preview = 1 + + call cursor(1, 10) + + ALEDetail + + let expected = ["Every statement should end with a semicolon", "second line"] + + AssertEqual 1, g:floating_preview_show_called + AssertEqual expected, g:floated_lines + +Execute(Floating preview is used with ALEDetail when g:ale_detail_to_floating_preview set): + let g:ale_detail_to_floating_preview = 1 + + call cursor(1, 10) + + ALEDetail + + let expected = ["Every statement should end with a semicolon", "second line"] + + AssertEqual 1, g:floating_preview_show_called + AssertEqual expected, g:floated_lines + +Execute(Floating preview is not used with ALEDetail by default): + call cursor(1, 10) + + ALEDetail + + AssertEqual 0, g:floating_preview_show_called diff --git a/test/test_hover.vader b/test/test_hover.vader index 9689cda2..7a9c8d91 100644 --- a/test/test_hover.vader +++ b/test/test_hover.vader @@ -7,9 +7,25 @@ Before: let g:item_list = [] let g:show_message_arg_list = [] + let g:ale_floating_preview = 0 + let g:ale_hover_to_floating_preview = 0 + let g:ale_detail_to_floating_preview = 0 + runtime autoload/ale/linter.vim runtime autoload/ale/lsp.vim + runtime autoload/ale/lsp_linter.vim runtime autoload/ale/util.vim + runtime autoload/ale/floating_preview.vim + runtime autoload/ale/hover.vim + + let g:floated_lines = [] + let g:floating_preview_show_called = 0 + + " Stub out so we can track the call + function! ale#floating_preview#Show(lines, ...) abort + let g:floating_preview_show_called = 1 + let g:floated_lines = a:lines + endfunction function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort let g:Callback = a:callback @@ -50,6 +66,7 @@ Before: \) endfunction + After: call ale#hover#SetMap({}) call ale#test#RestoreDirectory() @@ -65,6 +82,7 @@ After: runtime autoload/ale/lsp_linter.vim runtime autoload/ale/lsp.vim runtime autoload/ale/util.vim + runtime autoload/ale/floating_preview.vim Given python(Some Python file): foo @@ -101,7 +119,7 @@ Execute(tsserver quickinfo responses will null missing bodies should be handled) AssertEqual {}, ale#hover#GetMap() Execute(tsserver quickinfo displayString values should be displayed): - call ale#hover#SetMap({3: {}}) + call ale#hover#SetMap({3: {'buffer': bufnr('')}}) call ale#hover#HandleTSServerResponse( \ 1, \ { @@ -168,8 +186,30 @@ Execute(LSP hover response with lists of strings and marked strings should be ha \], g:show_message_arg_list AssertEqual {}, ale#hover#GetMap() +Execute(LSP hover with ale_floating_preview should float): + let g:ale_floating_preview = 1 + + call HandleValidLSPResult({'contents': "the message\ncontinuing"}) + + AssertEqual 1, g:floating_preview_show_called + AssertEqual ["the message", "continuing"], g:floated_lines + +Execute(LSP hover ale_hover_to_floating_preview should float): + let g:ale_hover_to_floating_preview = 1 + + call HandleValidLSPResult({'contents': "the message\ncontinuing"}) + + AssertEqual 1, g:floating_preview_show_called + AssertEqual ["the message", "continuing"], g:floated_lines + + +Execute(LSP hover by default should not float): + call HandleValidLSPResult({'contents': "the message\ncontinuing"}) + + AssertEqual 0, g:floating_preview_show_called + Execute(tsserver responses for documentation requests should be handled): - call ale#hover#SetMap({3: {'show_documentation': 1}}) + call ale#hover#SetMap({3: {'show_documentation': 1, 'buffer': bufnr('')}}) call ale#hover#HandleTSServerResponse( \ 1, @@ -187,3 +227,46 @@ Execute(tsserver responses for documentation requests should be handled): " The preview window should show the text. AssertEqual ['foo is a very good method'], ale#test#GetPreviewWindowText() silent! pclose + +Execute(hover with show_documentation should be in the preview window, not floating): + let g:ale_hover_to_floating_preview = 1 + let g:ale_floating_preview = 1 + + call ale#hover#SetMap({3: {'show_documentation': 1, 'buffer': bufnr('')}}) + + call ale#hover#HandleTSServerResponse( + \ 1, + \ { + \ 'command': 'quickinfo', + \ 'request_seq': 3, + \ 'success': v:true, + \ 'body': { + \ 'documentation': 'foo is a very good method', + \ 'displayString': 'foo bar ', + \ }, + \ } + \) + + let expected = ["Every statement should end with a semicolon", "second line"] + + AssertEqual 0, g:floating_preview_show_called + +Execute(TSServer hover without show_documentation and ale_floating_preview should float): + let g:ale_floating_preview = 1 + + call ale#hover#SetMap({3: {'buffer': bufnr('')}}) + + call ale#hover#HandleTSServerResponse( + \ 1, + \ { + \ 'command': 'quickinfo', + \ 'request_seq': 3, + \ 'success': v:true, + \ 'body': { + \ 'displayString': "the message\ncontinuing", + \ }, + \ } + \) + + AssertEqual 1, g:floating_preview_show_called + AssertEqual ["the message", "continuing"], g:floated_lines diff --git a/test/test_lint_on_enter_when_file_changed.vader b/test/test_lint_on_enter_when_file_changed.vader index 88493005..0d4c4af8 100644 --- a/test/test_lint_on_enter_when_file_changed.vader +++ b/test/test_lint_on_enter_when_file_changed.vader @@ -35,7 +35,7 @@ After: Execute(The file changed event function should set b:ale_file_changed): let g:ale_lint_on_enter = 0 - if has('gui') + if has('gui_running') new else e test diff --git a/test/test_linting_updates_loclist.vader b/test/test_linting_updates_loclist.vader index 8a162703..41c86522 100644 --- a/test/test_linting_updates_loclist.vader +++ b/test/test_linting_updates_loclist.vader @@ -7,10 +7,12 @@ Before: Save g:ale_run_synchronously Save g:ale_set_lists_synchronously Save g:ale_buffer_info + Save g:ale_sign_offset " We want to check that sign IDs are set for this test. let g:ale_set_signs = 1 let g:ale_set_loclist = 1 + let g:ale_sign_offset = 2000000 " Disable features we don't need for these tests. let g:ale_set_quickfix = 0 let g:ale_set_highlights = 0 @@ -77,7 +79,7 @@ Execute(The loclist should be updated after linting is done): \ 'type': 'W', \ 'col': 10, \ 'text': 'Infix operators must be spaced. [Warning/space-infix-ops]', - \ 'sign_id': 1000001, + \ 'sign_id': 2000001, \ }, \ { \ 'lnum': 2, @@ -88,7 +90,7 @@ Execute(The loclist should be updated after linting is done): \ 'type': 'E', \ 'col': 10, \ 'text': 'Missing semicolon. [Error/semi]', - \ 'sign_id': 1000002, + \ 'sign_id': 2000002, \ } \ ], \ get(get(g:ale_buffer_info, bufnr('%'), {}), 'loclist', []) diff --git a/test/test_redundant_tsserver_rendering_avoided.vader b/test/test_redundant_tsserver_rendering_avoided.vader index 6125ebc2..bde5d152 100644 --- a/test/test_redundant_tsserver_rendering_avoided.vader +++ b/test/test_redundant_tsserver_rendering_avoided.vader @@ -111,7 +111,7 @@ Execute(An initial list of semantic errors should be handled): Assert g:ale_handle_loclist_called -Execute(Subsequent empty lists should be ignored): +Execute(Subsequent empty lists should be ignored - semantic): let g:ale_buffer_info[bufnr('')].semantic_loclist = [] call ale#lsp_linter#HandleLSPResponse(1, CreateError('semanticDiag', '')) @@ -138,3 +138,31 @@ Execute(Non-empty then non-empty semantic errors should be handled): call ale#lsp_linter#HandleLSPResponse(1, CreateError('semanticDiag', 'x')) Assert g:ale_handle_loclist_called + +Execute(Subsequent empty lists should be ignored - suggestion): + let g:ale_buffer_info[bufnr('')].suggestion_loclist = [] + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('suggestionDiag', '')) + + Assert !g:ale_handle_loclist_called + +Execute(Empty then non-empty suggestion messages should be handled): + let g:ale_buffer_info[bufnr('')].suggestion_loclist = [] + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('suggestionDiag', 'x')) + + Assert g:ale_handle_loclist_called + +Execute(Non-empty then empt suggestion messages should be handled): + let g:ale_buffer_info[bufnr('')].suggestion_loclist = CreateLoclist('x') + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('suggestionDiag', '')) + + Assert g:ale_handle_loclist_called + +Execute(Non-empty then non-empty suggestion messages should be handled): + let g:ale_buffer_info[bufnr('')].suggestion_loclist = CreateLoclist('x') + + call ale#lsp_linter#HandleLSPResponse(1, CreateError('suggestionDiag', 'x')) + + Assert g:ale_handle_loclist_called diff --git a/test/test_rename.vader b/test/test_rename.vader index 2e8b746e..5bc655f4 100644 --- a/test/test_rename.vader +++ b/test/test_rename.vader @@ -269,7 +269,7 @@ Execute(tsserver rename requests should be sent): \ }] \ ], \ g:message_list - AssertEqual {'42': {'old_name': 'somelongerline', 'new_name': 'a-new-name', 'force_save': 0}}, + AssertEqual {'42': {'old_name': 'somelongerline', 'new_name': 'a-new-name'}}, \ ale#rename#GetMap() Given python(Some Python file): @@ -470,7 +470,7 @@ Execute(LSP rename requests should be sent): let b:ale_linters = ['pyls'] call setpos('.', [bufnr(''), 1, 5, 0]) - ALERename! + ALERename " We shouldn't register the callback yet. AssertEqual '''''', string(g:Callback) @@ -500,5 +500,5 @@ Execute(LSP rename requests should be sent): \ ], \ g:message_list - AssertEqual {'42': {'old_name': 'foo', 'new_name': 'a-new-name', 'force_save': 1}}, + AssertEqual {'42': {'old_name': 'foo', 'new_name': 'a-new-name'}}, \ ale#rename#GetMap() diff --git a/test/test_shell_detection.vader b/test/test_shell_detection.vader index 697054d0..11d801c3 100644 --- a/test/test_shell_detection.vader +++ b/test/test_shell_detection.vader @@ -127,3 +127,51 @@ Execute(The dash dialect should be used for the shell and the base function): Execute(dash should be used for shellcheck): AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a Bash shellcheck shell directive): + # shellcheck shell=bash + +Execute(bash dialect should be detected appropriately): + AssertEqual 'bash', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a sh shellcheck shell directive): + #shellcheck shell=sh + +Execute(sh dialect should be detected appropriately): + AssertEqual 'sh', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a tcsh shellcheck shell directive): + # shellcheck shell=tcsh + +Execute(tcsh dialect should be detected appropriately): + AssertEqual 'tcsh', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a zsh shellcheck shell directive): + # shellcheck shell=zsh + +Execute(zsh dialect should be detected appropriately): + AssertEqual 'zsh', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a csh shellcheck shell directive): + # shellcheck shell=csh + +Execute(zsh dialect should be detected appropriately): + AssertEqual 'csh', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a ksh shellcheck shell directive): + # shellcheck shell=ksh + +Execute(ksh dialect should be detected appropriately): + AssertEqual 'ksh', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a dash shellcheck shell directive): + # shellcheck shell=dash + +Execute(dash dialect should be detected appropriately): + AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a ash shellcheck shell directive): + # shellcheck shell=ash + +Execute(dash dialect should be detected for ash that shellcheck does not support): + AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) diff --git a/test/test_socket_connections.vader b/test/test_socket_connections.vader index 6837fe51..9ea5580d 100644 --- a/test/test_socket_connections.vader +++ b/test/test_socket_connections.vader @@ -28,11 +28,17 @@ Before: endfunction let g:port = 10347 - let g:pid = str2nr(system( + let g:pid_tcp = str2nr(system( \ 'python' \ . ' ' . ale#Escape(g:dir . '/dumb_tcp_server.py') \ . ' ' . g:port \)) + let g:pipe_path = 'tmp_named_pipe' + let g:pid_pipe = str2nr(system( + \ 'python' + \ . ' ' . ale#Escape(g:dir . '/dumb_named_pipe_server.py') + \ . ' ' . g:pipe_path + \)) endif After: @@ -46,17 +52,23 @@ After: delfunction WaitForData delfunction TestCallback - if has_key(g:, 'pid') - call system('kill ' . g:pid) + if has_key(g:, 'pid_tcp') + call system('kill ' . g:pid_tcp) endif - unlet! g:pid + if has_key(g:, 'pid_pipe') + call system('kill ' . g:pid_pipe) + endif + + unlet! g:pid_tcp unlet! g:port + unlet! g:pid_pipe + unlet! g:pipe_path endif unlet! g:can_run_socket_tests -Execute(Sending and receiving connections to sockets should work): +Execute(Sending and receiving connections to tcp sockets should work): if g:can_run_socket_tests let g:channel_id = ale#socket#Open( \ '127.0.0.1:' . g:port, @@ -90,3 +102,38 @@ Execute(Sending and receiving connections to sockets should work): \ {'callback': function('function')} \) endif + +Execute(Sending and receiving connections to named pipe sockets should work): + if g:can_run_socket_tests && has('nvim') + let g:channel_id = ale#socket#Open( + \ g:pipe_path, + \ {'callback': function('TestCallback')} + \) + + Assert g:channel_id >= 0, 'The socket was not opened!' + + call ale#socket#Send(g:channel_id, 'hello') + call ale#socket#Send(g:channel_id, ' world') + + AssertEqual 1, ale#socket#IsOpen(g:channel_id) + + " Wait up to 1 second for the expected data to arrive. + call WaitForData('hello world', 1000) + + AssertEqual g:channel_id, g:channel_id_received + AssertEqual 'hello world', g:data_received + AssertEqual g:pipe_path, ale#socket#GetAddress(g:channel_id) + + call ale#socket#Close(g:channel_id) + + AssertEqual 0, ale#socket#IsOpen(g:channel_id) + AssertEqual '', ale#socket#GetAddress(g:channel_id) + endif + + " NeoVim versions which can't connect to sockets should just fail. + if has('nvim') && !exists('*chanclose') + AssertEqual -1, ale#socket#Open( + \ 'tmp_named_pipe', + \ {'callback': function('function')} + \) + endif diff --git a/test/typescript/test.ts b/test/typescript/test.ts new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/typescript/test.ts diff --git a/test/typescript/tsconfig.json b/test/typescript/tsconfig.json new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/typescript/tsconfig.json diff --git a/test/xo-test-files/monorepo/node_modules/xo/cli.js b/test/xo-test-files/monorepo/node_modules/xo/cli.js new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/xo-test-files/monorepo/node_modules/xo/cli.js diff --git a/test/xo-test-files/monorepo/package.json b/test/xo-test-files/monorepo/package.json new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/xo-test-files/monorepo/package.json diff --git a/test/xo-test-files/monorepo/packages/a/index.js b/test/xo-test-files/monorepo/packages/a/index.js new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/xo-test-files/monorepo/packages/a/index.js diff --git a/test/xo-test-files/monorepo/packages/a/index.ts b/test/xo-test-files/monorepo/packages/a/index.ts new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/xo-test-files/monorepo/packages/a/index.ts diff --git a/test/xo-test-files/monorepo/packages/a/package.json b/test/xo-test-files/monorepo/packages/a/package.json new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/xo-test-files/monorepo/packages/a/package.json |