From e4ac5d9dffdf6780cc5808be4653a0fe898ba799 Mon Sep 17 00:00:00 2001 From: Vinicius Stock Date: Fri, 5 Sep 2025 10:52:19 -0400 Subject: [PATCH] Escape file paths for test command resolution --- .../ruby_lsp_rails/rails_test_style.rb | 16 ++- test/ruby_lsp_rails/rails_test_style_test.rb | 130 ++++++++++++++++++ 2 files changed, 139 insertions(+), 7 deletions(-) diff --git a/lib/ruby_lsp/ruby_lsp_rails/rails_test_style.rb b/lib/ruby_lsp/ruby_lsp_rails/rails_test_style.rb index 2a61d404..34474fbe 100644 --- a/lib/ruby_lsp/ruby_lsp_rails/rails_test_style.rb +++ b/lib/ruby_lsp/ruby_lsp_rails/rails_test_style.rb @@ -26,17 +26,19 @@ def resolve_test_commands(items) if tags.include?("test_dir") if children.empty? - full_files.concat(Dir.glob( - "#{path}/**/{*_test,test_*}.rb", - File::Constants::FNM_EXTGLOB | File::Constants::FNM_PATHNAME, - )) + full_files.concat( + Dir.glob( + "#{path}/**/{*_test,test_*}.rb", + File::Constants::FNM_EXTGLOB | File::Constants::FNM_PATHNAME, + ).map! { |f| Shellwords.escape(f) }, + ) end elsif tags.include?("test_file") - full_files << path if children.empty? + full_files << Shellwords.escape(path) if children.empty? elsif tags.include?("test_group") - commands << "#{BASE_COMMAND} #{path} --name \"/#{Shellwords.escape(item[:id])}(#|::)/\"" + commands << "#{BASE_COMMAND} #{Shellwords.escape(path)} --name \"/#{Shellwords.escape(item[:id])}(#|::)/\"" else - full_files << "#{path}:#{item.dig(:range, :start, :line) + 1}" + full_files << "#{Shellwords.escape(path)}:#{item.dig(:range, :start, :line) + 1}" end queue.concat(children) diff --git a/test/ruby_lsp_rails/rails_test_style_test.rb b/test/ruby_lsp_rails/rails_test_style_test.rb index 1e651a14..9b9fb480 100644 --- a/test/ruby_lsp_rails/rails_test_style_test.rb +++ b/test/ruby_lsp_rails/rails_test_style_test.rb @@ -228,6 +228,136 @@ class SpecialCharsTest < ActiveSupport::TestCase end end + test "resolve test escapes file paths in groups" do + with_server do |server| + sleep(0.1) while RubyLsp::Addon.addons.first.instance_variable_get(:@rails_runner_client).is_a?(NullClient) + + server.process_message({ + id: 1, + method: "rubyLsp/resolveTestCommands", + params: { + items: [ + { + id: "GroupTest", + uri: "file:///test/group(v2)_test.rb", + label: "GroupTest", + range: { + start: { line: 0, character: 0 }, + end: { line: 30, character: 3 }, + }, + tags: ["framework:rails", "test_group"], + children: [], + }, + ], + }, + }) + + result = pop_result(server) + response = result.response + + assert_equal( + ["#{RailsTestStyle::BASE_COMMAND} /test/group\\(v2\\)_test.rb --name \"/GroupTest(#|::)/\""], + response[:commands], + ) + end + end + + test "resolve test escapes single file paths" do + with_server do |server| + sleep(0.1) while RubyLsp::Addon.addons.first.instance_variable_get(:@rails_runner_client).is_a?(NullClient) + + server.process_message({ + id: 1, + method: "rubyLsp/resolveTestCommands", + params: { + items: [ + { + id: "file:///test/example(v2)_test.rb", + uri: "file:///test/example(v2)_test.rb", + label: "/test/example(v2)_test.rb", + tags: ["framework:rails", "test_file"], + children: [], + }, + ], + }, + }) + + result = pop_result(server) + response = result.response + + assert_equal( + ["#{RailsTestStyle::BASE_COMMAND} /test/example\\(v2\\)_test.rb"], + response[:commands], + ) + end + end + + test "resolve test escapes file paths inside directories" do + Dir.stubs(:glob).returns(["/test/example(v2)_test.rb"]) + + with_server do |server| + sleep(0.1) while RubyLsp::Addon.addons.first.instance_variable_get(:@rails_runner_client).is_a?(NullClient) + + server.process_message({ + id: 1, + method: "rubyLsp/resolveTestCommands", + params: { + items: [ + { + id: "file:///test", + uri: "file:///test", + label: "/test", + tags: ["test_dir", "framework:rails"], + children: [], + }, + ], + }, + }) + + result = pop_result(server) + response = result.response + + assert_equal( + ["#{RailsTestStyle::BASE_COMMAND} /test/example\\(v2\\)_test.rb"], + response[:commands], + ) + end + end + + test "resolve test escapes file paths for specific examples" do + with_server do |server| + sleep(0.1) while RubyLsp::Addon.addons.first.instance_variable_get(:@rails_runner_client).is_a?(NullClient) + + server.process_message({ + id: 1, + method: "rubyLsp/resolveTestCommands", + params: { + items: [ + { + id: "ExampleTest#test_something", + uri: "file:///test/example(v2)_test.rb", + label: "test something", + tags: ["framework:rails"], + range: { + start: { line: 10, character: 0 }, + end: { line: 15, character: 3 }, + }, + children: [], + }, + ], + }, + }) + + result = pop_result(server) + response = result.response + + assert_equal( + ["#{RailsTestStyle::BASE_COMMAND} /test/example\\(v2\\)_test.rb:11"], + response[:commands], + ) + end + end + test "tests with backslashes" do source = File.read(File.join(__dir__, "..", "fixtures", "test_with_escaped_quotes.rb"))