Skip to content

Commit ccb751d

Browse files
author
Cody Mikol
committed
more stuff
1 parent 830aec7 commit ccb751d

File tree

2 files changed

+170
-66
lines changed

2 files changed

+170
-66
lines changed

lua/kotest-treesitter-query.lua

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
local TreesitterQuery = {}
2+
3+
-- These are treesitter queries for pulling data of of the AST,
4+
-- More information on how this works over here: https://neovim.io/doc/user/treesitter.html
5+
-- And you can interactively play around with these kotlin queries here: https://fwcd.dev/tree-sitter-kotlin/
6+
7+
TreesitterQuery.value = [[
8+
9+
;; --- DESCRIBE SPEC ---
10+
11+
; Matches describe("context") { /** body **/ }
12+
13+
(call_expression
14+
(call_expression
15+
(simple_identifier) @func_name (#eq? @func_name "describe")
16+
(call_suffix
17+
(value_arguments
18+
(value_argument
19+
(string_literal) @namespace.name
20+
)
21+
)
22+
)
23+
)
24+
) @namespace.definition
25+
26+
; Matches it("context") { /** body **/ }
27+
28+
(call_expression
29+
(call_expression
30+
(simple_identifier) @func_name (#eq? @func_name "it")
31+
(call_suffix
32+
(value_arguments
33+
(value_argument
34+
(string_literal) @test.name
35+
)
36+
)
37+
)
38+
)
39+
) @test.definition
40+
41+
;; -- todo FUN SPEC --
42+
;; -- todo SHOULD SPEC --
43+
;; -- todo STRING SPEC --
44+
;; -- todo BEHAVIOR SPEC --
45+
;; -- todo FREE SPEC --
46+
;; -- todo WORD SPEC --
47+
;; -- todo FEATURE SPEC --
48+
;; -- todo EXPECT SPEC --
49+
;; -- todo ANNOTATION SPEC --
50+
51+
]]
52+
53+
return TreesitterQuery

lua/neotest-kotest.lua

Lines changed: 117 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
local neotest = require("neotest")
21
local lib = require("neotest.lib")
32
local async = require("neotest.async")
43

4+
local treesitter_query = require("kotest-treesitter-query")
5+
56
local adapter = { name = "neotest-kotest" }
67

78
---Find the project root directory given a current directory to work from.
@@ -10,7 +11,7 @@ local adapter = { name = "neotest-kotest" }
1011
---@param dir string @Directory to treat as cwd
1112
---@return string | nil @Absolute root dir of test suite
1213
function adapter.root(dir)
13-
return lib.files.match_root_pattern("build.gradle?(.kts)")(dir)
14+
return lib.files.match_root_pattern("build.gradle.kts")(dir)
1415
end
1516

1617
local ignored_directories =
@@ -39,87 +40,128 @@ function adapter.is_test_file(file_path)
3940
return false
4041
end
4142

42-
-- todo(mikol): see if this has a kotest import...
43+
if not vim.endswith(file_path, ".kt") then
44+
return false
45+
end
46+
47+
if string.find(file_path, "src/main") then
48+
return false
49+
end
50+
4351
return true
4452
end
4553

54+
local function get_match_type(captured_nodes)
55+
if captured_nodes["namespace.name"] then
56+
return "namespace"
57+
end
58+
if captured_nodes["test.name"] then
59+
return "test"
60+
end
61+
end
62+
63+
function adapter.build_position(file_path, source, captured_nodes)
64+
local match_type = get_match_type(captured_nodes)
65+
local definition = captured_nodes[match_type .. ".definition"]
66+
67+
local build_position = {
68+
type = match_type,
69+
path = file_path,
70+
range = { definition:range() },
71+
}
72+
73+
return build_position
74+
end
75+
76+
---Given a file path, parse all the tests within it.
4677
---@async
78+
---@param file_path string Absolute file path
79+
---@return neotest.Tree | nil
4780
function adapter.discover_positions(path)
48-
local query = [[
49-
50-
;; --- DESCRIBE SPEC ---
51-
52-
; Matches describe("context") { /** body **/ }
53-
54-
(call_expression
55-
(call_expression
56-
(simple_identifier) @func_name (#eq? @func_name "describe")
57-
(call_suffix
58-
(value_arguments
59-
(value_argument
60-
(string_literal) @namespace.name
61-
)
62-
)
63-
)
64-
)
65-
) @namespace.definition
66-
67-
; Matches it("context") { /** body **/ }
68-
69-
(call_expression
70-
(call_expression
71-
(simple_identifier) @func_name (#eq? @func_name "it")
72-
(call_suffix
73-
(value_arguments
74-
(value_argument
75-
(string_literal) @namespace.name
76-
)
77-
)
78-
)
79-
)
80-
) @namespace.definition
81-
82-
;; -- todo FUN SPEC --
83-
;; -- todo SHOULD SPEC --
84-
;; -- todo STRING SPEC --
85-
;; -- todo BEHAVIOR SPEC --
86-
;; -- todo FREE SPEC --
87-
;; -- todo WORD SPEC --
88-
;; -- todo FEATURE SPEC --
89-
;; -- todo EXPECT SPEC --
90-
;; -- todo ANNOTATION SPEC --
91-
]]
92-
93-
return lib.treesitter.parse_positions(path, query, { nested_namespaces = true })
81+
local positions = lib.treesitter.parse_positions(path, treesitter_query.value, {
82+
nested_namespaces = true,
83+
nested_tests = false,
84+
-- build_position = 'require("neotest-kotest").build_position',
85+
})
86+
87+
return positions
88+
end
89+
90+
function get_package_name(file_path)
91+
local package_name_query = "(package_header (identifier) @package.name)"
92+
93+
local file = io.open(file_path)
94+
95+
if file == nil then
96+
return "*"
97+
end
98+
99+
local code = file:read("*all")
100+
101+
local new_buffer_number = vim.api.nvim_create_buf(false, true)
102+
vim.api.nvim_buf_set_lines(new_buffer_number, 0, -1, false, vim.split(code, "\n"))
103+
104+
file:close()
105+
106+
local language = "kotlin"
107+
108+
local parser = vim.treesitter.get_string_parser(code, language)
109+
local tree = parser:parse()
110+
local root = tree[1]:root()
111+
112+
local query = vim.treesitter.query.parse(language, package_name_query)
113+
114+
for _, match, _ in query:iter_matches(root, new_buffer_number, root:start(), root:end_()) do
115+
for _, node in pairs(match) do
116+
local start_row, start_col = node:start()
117+
local end_row, end_col = node:end_()
118+
119+
-- string:sub is 1 indexed, but the nodes apis return 0 indexed jawns...
120+
-- effectively making this a river of brain melting sadness
121+
local text = code:sub(start_row + 2, end_row - 1):sub(start_col, end_col - 1)
122+
123+
return text
124+
end
125+
end
126+
127+
-- local package_name = matches[0].captures["package.name"][1]
128+
129+
-- vim.inspect(package_name)
130+
131+
return nil
94132
end
95133

96-
---@param args neotest.RunArgs
134+
---@param args neotest.run.RunArgs
97135
---@return nil | neotest.RunSpec | neotest.RunSpec[]
98136
function adapter.build_spec(args)
99137
local results_path = async.fn.tempname() .. ".json"
138+
139+
-- Write something so there is a place to stream to...
140+
lib.files.write(results_path, "")
141+
100142
local tree = args.tree
101143

102144
if not tree then
103145
return
104146
end
105147

106-
local pos = args.tree:data()
148+
local pos = tree:data()
107149

108150
local root = adapter.root(pos.path)
151+
local spec = get_package_name(pos.path)
152+
local test = "*"
109153

110-
local package = get_test_package(args)
111-
local test_name = string.sub()
112-
113-
local command = "/.gradlew test -Dkotest.filter.specs='"
114-
.. package
115-
.. "' -Dkotest.filter.tests='"
116-
.. test_name
117-
.. "'"
154+
local command_three = "export kotest_filter_tests='"
155+
.. test
156+
.. "'; export kotest_filter_specs='"
157+
.. spec
158+
.. "'; ./gradlew clean test --info >> "
159+
.. results_path
118160

119161
local stream_data, stop_stream = lib.files.stream(results_path)
120162

121163
return {
122-
command = command,
164+
command = command_three,
123165
cwd = root,
124166
context = {
125167
results_path = results_path,
@@ -128,14 +170,19 @@ function adapter.build_spec(args)
128170
},
129171
stream = function()
130172
return function()
173+
print("streaming...")
131174
local new_results = stream_data()
132-
local ok, parsed = pcall(vim.json.decode, new_results, { luanil = { object = true } })
133175

134-
if not ok or not parsed.testResults then
135-
return {}
136-
end
176+
local tests = {}
177+
178+
tests["foo"] = {
179+
status = "skipped",
180+
short = "something goofy",
181+
output = "comnsole out",
182+
location = "test",
183+
}
137184

138-
return {}
185+
return tests
139186
end
140187
end,
141188
}
@@ -147,8 +194,12 @@ end
147194
---@param tree neotest.Tree
148195
---@return table<string, neotest.Result>
149196
function adapter.results(spec, result, tree)
197+
print("In results")
198+
199+
print(result)
200+
150201
spec.context.stop_stream()
151-
return "something"
202+
return { "test", {} }
152203
end
153204

154205
return adapter

0 commit comments

Comments
 (0)