Skip to content

Commit

Permalink
Add test items collection builder
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed Feb 12, 2025
1 parent f296ba4 commit debbee7
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/ruby_lsp/internal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
require "ruby_lsp/response_builders/hover"
require "ruby_lsp/response_builders/semantic_highlighting"
require "ruby_lsp/response_builders/signature_help"
require "ruby_lsp/response_builders/test_items"

# Request support
require "ruby_lsp/requests/support/selection_range"
Expand All @@ -60,6 +61,7 @@
require "ruby_lsp/requests/support/rubocop_runner"
require "ruby_lsp/requests/support/rubocop_formatter"
require "ruby_lsp/requests/support/syntax_tree_formatter"
require "ruby_lsp/requests/support/test_item"

# Requests
require "ruby_lsp/requests/request"
Expand Down
54 changes: 54 additions & 0 deletions lib/ruby_lsp/requests/support/test_item.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# typed: strict
# frozen_string_literal: true

module RubyLsp
module Requests
module Support
# Represents a test item as defined by the VS Code interface to be used in the test explorer
# See https://code.visualstudio.com/api/references/vscode-api#TestItem
class TestItem
extend T::Sig

sig { returns(String) }
attr_reader :id, :label

sig { params(id: String, label: String, uri: URI::Generic, range: Interface::Range).void }
def initialize(id, label, uri, range)
@id = id
@label = label
@uri = uri
@range = range
@children = T.let({}, T::Hash[String, TestItem])
end

sig { params(item: TestItem).void }
def <<(item)
raise ArgumentError, "TestItem ID is already in use" if @children.key?(item.id)

@children[item.id] = item
end

sig { params(id: String).returns(T.nilable(TestItem)) }
def [](id)
@children[id]
end

sig { returns(T::Array[TestItem]) }
def children
@children.values
end

sig { returns(T::Hash[Symbol, T.untyped]) }
def to_hash
{
id: @id,
label: @label,
uri: @uri,
range: @range,
children: @children.values.map(&:to_hash),
}
end
end
end
end
end
36 changes: 36 additions & 0 deletions lib/ruby_lsp/response_builders/test_items.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# typed: strict
# frozen_string_literal: true

module RubyLsp
module ResponseBuilders
class TestItems < ResponseBuilder
extend T::Sig
extend T::Generic

ResponseType = type_member { { fixed: Requests::Support::TestItem } }

sig { void }
def initialize
super
@items = T.let({}, T::Hash[String, ResponseType])
end

sig { params(item: ResponseType).void }
def <<(item)
raise ArgumentError, "TestItem ID is already in use" if @items.key?(item.id)

@items[item.id] = item
end

sig { params(id: String).returns(T.nilable(ResponseType)) }
def [](id)
@items[id]
end

sig { override.returns(T::Array[ResponseType]) }
def response
@items.values
end
end
end
end
58 changes: 58 additions & 0 deletions test/response_builders/test_items_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# typed: true
# frozen_string_literal: true

require "test_helper"

module RubyLsp
class TestItemsTest < Minitest::Test
def setup
@uri = URI::Generic.from_path(path: "/fake_test.rb")
@range = Interface::Range.new(
start: Interface::Position.new(line: 0, character: 0),
end: Interface::Position.new(line: 10, character: 3),
)
end

def test_allows_building_hierarchy_of_tests
builder = ResponseBuilders::TestItems.new
test_item = Requests::Support::TestItem.new("my-id", "Test label", @uri, @range)
nested_item = Requests::Support::TestItem.new("nested-id", "Nested label", @uri, @range)

builder << test_item
test_item << nested_item

item = builder["my-id"]
assert(item)
assert(T.must(item)["nested-id"])

builder.response.map(&:to_hash).each { |item| assert_expected_fields(item) }
end

def test_raises_if_trying_to_add_item_with_same_id
builder = ResponseBuilders::TestItems.new
test_item = Requests::Support::TestItem.new("my-id", "Test label", @uri, @range)
nested_item = Requests::Support::TestItem.new("nested-id", "Nested label", @uri, @range)

builder << test_item
test_item << nested_item

assert_raises(ArgumentError) do
builder << Requests::Support::TestItem.new("my-id", "Other title, but same ID", @uri, @range)
end

assert_raises(ArgumentError) do
test_item << Requests::Support::TestItem.new("nested-id", "Other title, but same ID", @uri, @range)
end
end

private

def assert_expected_fields(hash)
[:id, :label, :uri, :range, :children].each do |field|
assert(hash[field])
end

hash[:children].each { |child| assert_expected_fields(child) }
end
end
end

0 comments on commit debbee7

Please sign in to comment.