Skip to content

Commit 4acddc0

Browse files
fix: only group specs if the context or describe call has a block (#87)
* fix: only group specs if the `context` or `describe` call has a block * Add new specs * Run a rubocop auto-correct pass
1 parent d710654 commit 4acddc0

File tree

4 files changed

+234
-2
lines changed

4 files changed

+234
-2
lines changed

lib/ruby_lsp/ruby_lsp_rspec/code_lens.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,12 @@ def log_message(message)
5555
puts "[#{self.class}]: #{message}"
5656
end
5757

58+
# A node is valid if it has a block and the receiver is RSpec (or nil)
5859
#: (Prism::CallNode) -> bool
5960
def valid_group?(node)
60-
!(node.block.nil? || (node.receiver && node.receiver&.slice != "RSpec"))
61+
return false if node.block.nil?
62+
63+
node.receiver.nil? || node.receiver&.slice == "RSpec"
6164
end
6265

6366
#: (Prism::CallNode) -> String

lib/ruby_lsp/ruby_lsp_rspec/test_discovery.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def on_call_node_enter(node)
3030

3131
case node.message
3232
when "describe", "context"
33+
return unless valid_group?(node)
34+
3335
handle_describe(node)
3436
when "it", "specify", "example"
3537
handle_example(node)
@@ -116,9 +118,12 @@ def find_parent_test_group
116118
@group_stack.last
117119
end
118120

121+
# A node is valid if it has a block and the receiver is RSpec (or nil)
119122
#: (Prism::CallNode) -> bool
120123
def valid_group?(node)
121-
!(node.block.nil? || (node.receiver && node.receiver&.slice != "RSpec"))
124+
return false if node.block.nil?
125+
126+
node.receiver.nil? || node.receiver&.slice == "RSpec"
122127
end
123128

124129
#: (Prism::CallNode) -> String

spec/code_lens_spec.rb

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,122 @@ def dummy
229229
end
230230
end
231231

232+
it "ignores describe and context calls without blocks" do
233+
source = <<~RUBY
234+
RSpec.describe "Valid group with block" do
235+
it "test in valid group" do
236+
end
237+
end
238+
239+
# These should be ignored because they don't have blocks
240+
RSpec.describe "Invalid group without block"
241+
RSpec.context "Another invalid group"
242+
243+
# This should also work with non-RSpec receivers
244+
describe "Valid group without RSpec prefix" do
245+
it "test in valid group" do
246+
end
247+
end
248+
249+
# Invalid without block, even without RSpec prefix
250+
describe "Invalid without block"
251+
context "Invalid context without block"
252+
RUBY
253+
254+
with_server(source, uri) do |server, uri|
255+
server.process_message(
256+
{
257+
id: 1,
258+
method: "textDocument/codeLens",
259+
params: {
260+
textDocument: { uri: uri },
261+
position: { line: 0, character: 0 },
262+
},
263+
},
264+
)
265+
266+
response = pop_result(server).response
267+
268+
# Should only generate code lens for the 2 valid groups (with blocks) and their children
269+
# Each group gets 3 code lenses (run, run in terminal, debug)
270+
# Each example gets 3 code lenses
271+
# So: 2 groups * 3 + 2 examples * 3 = 12 total
272+
expect(response.count).to eq(12)
273+
274+
# Verify the valid groups are present
275+
group_commands = response.select { |r| r.data[:kind] == :group }
276+
expect(group_commands.count).to eq(6) # 2 groups * 3 commands each
277+
278+
# Check that the correct groups are present
279+
group_names = group_commands.map { |cmd| cmd.command.arguments[1] }.uniq
280+
expect(group_names).to contain_exactly("Valid group with block", "Valid group without RSpec prefix")
281+
282+
# Verify examples are present
283+
example_commands = response.select { |r| r.data[:kind] == :example }
284+
expect(example_commands.count).to eq(6) # 2 examples * 3 commands each
285+
end
286+
end
287+
288+
it "handles nested groups where some lack blocks" do
289+
source = <<~RUBY
290+
RSpec.describe "Outer group with block" do
291+
# Valid nested group
292+
describe "Valid nested group" do
293+
it "nested test" do
294+
end
295+
end
296+
297+
# Invalid nested group (no block) - should be ignored
298+
describe "Invalid nested group"
299+
300+
# Another valid nested group
301+
context "Valid context" do
302+
it "context test" do
303+
end
304+
end
305+
306+
# Invalid context (no block) - should be ignored
307+
context "Invalid context"
308+
end
309+
RUBY
310+
311+
with_server(source, uri) do |server, uri|
312+
server.process_message(
313+
{
314+
id: 1,
315+
method: "textDocument/codeLens",
316+
params: {
317+
textDocument: { uri: uri },
318+
position: { line: 0, character: 0 },
319+
},
320+
},
321+
)
322+
323+
response = pop_result(server).response
324+
325+
# Should generate code lens for:
326+
# - 1 outer group (3 commands)
327+
# - 2 valid nested groups (2 * 3 = 6 commands)
328+
# - 2 examples (2 * 3 = 6 commands)
329+
# Total: 15 commands
330+
expect(response.count).to eq(15)
331+
332+
# Check that only the valid groups are present
333+
group_commands = response.select { |r| r.data[:kind] == :group }
334+
expect(group_commands.count).to eq(9) # 3 groups * 3 commands each
335+
336+
group_names = group_commands.map { |cmd| cmd.command.arguments[1] }.uniq
337+
expect(group_names).to contain_exactly("Outer group with block", "Valid nested group", "Valid context")
338+
339+
# Verify examples are present
340+
example_commands = response.select { |r| r.data[:kind] == :example }
341+
expect(example_commands.count).to eq(6) # 2 examples * 3 commands each
342+
343+
example_names = example_commands.map { |cmd| cmd.command.arguments[1] }.uniq
344+
expect(example_names).to contain_exactly("nested test", "context test")
345+
end
346+
end
347+
232348
context "with a custom rspec command configured" do
233349
let(:configuration) do
234350
{

spec/test_discovery_spec.rb

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,5 +178,113 @@
178178
expect(third_example[:label]).to eq("example at ./spec/fake_spec.rb:10")
179179
end
180180
end
181+
182+
it "ignores describe and context calls without blocks" do
183+
source = <<~RUBY
184+
RSpec.describe "Valid group with block" do
185+
it "test in valid group" do
186+
expect(true).to be(true)
187+
end
188+
end
189+
190+
# These should be ignored because they don't have blocks
191+
RSpec.describe "Invalid group without block"
192+
RSpec.context "Another invalid group"
193+
194+
# This should also work with non-RSpec receivers
195+
describe "Valid group without RSpec prefix" do
196+
it "test in valid group" do
197+
expect(true).to be(true)
198+
end
199+
end
200+
201+
# Invalid without block, even without RSpec prefix
202+
describe "Invalid without block"
203+
context "Invalid context without block"
204+
RUBY
205+
206+
with_server(source, uri) do |server, uri|
207+
server.process_message(
208+
{
209+
id: 1,
210+
method: "rubyLsp/discoverTests",
211+
params: {
212+
textDocument: { uri: uri },
213+
},
214+
},
215+
)
216+
217+
items = pop_result(server).response
218+
219+
# Should only find 2 valid groups (the ones with blocks)
220+
expect(items.length).to eq(2)
221+
222+
first_group = items.first
223+
expect(first_group[:label]).to eq("Valid group with block")
224+
expect(first_group[:children].length).to eq(1)
225+
expect(first_group[:children][0][:label]).to eq("test in valid group")
226+
227+
second_group = items[1]
228+
expect(second_group[:label]).to eq("Valid group without RSpec prefix")
229+
expect(second_group[:children].length).to eq(1)
230+
expect(second_group[:children][0][:label]).to eq("test in valid group")
231+
end
232+
end
233+
234+
it "handles nested groups where some lack blocks" do
235+
source = <<~RUBY
236+
RSpec.describe "Outer group with block" do
237+
# Valid nested group
238+
describe "Valid nested group" do
239+
it "nested test" do
240+
expect(true).to be(true)
241+
end
242+
end
243+
244+
# Invalid nested group (no block) - should be ignored
245+
describe "Invalid nested group"
246+
247+
# Another valid nested group
248+
context "Valid context" do
249+
it "context test" do
250+
expect(true).to be(true)
251+
end
252+
end
253+
254+
# Invalid context (no block) - should be ignored
255+
context "Invalid context"
256+
end
257+
RUBY
258+
259+
with_server(source, uri) do |server, uri|
260+
server.process_message(
261+
{
262+
id: 1,
263+
method: "rubyLsp/discoverTests",
264+
params: {
265+
textDocument: { uri: uri },
266+
},
267+
},
268+
)
269+
270+
items = pop_result(server).response
271+
272+
expect(items.length).to eq(1)
273+
274+
outer_group = items.first
275+
expect(outer_group[:label]).to eq("Outer group with block")
276+
# Should only have 2 children (the valid nested groups)
277+
expect(outer_group[:children].length).to eq(2)
278+
279+
nested_groups = outer_group[:children]
280+
expect(nested_groups[0][:label]).to eq("Valid nested group")
281+
expect(nested_groups[0][:children].length).to eq(1)
282+
expect(nested_groups[0][:children][0][:label]).to eq("nested test")
283+
284+
expect(nested_groups[1][:label]).to eq("Valid context")
285+
expect(nested_groups[1][:children].length).to eq(1)
286+
expect(nested_groups[1][:children][0][:label]).to eq("context test")
287+
end
288+
end
181289
end
182290
end

0 commit comments

Comments
 (0)