Skip to content

Commit

Permalink
register ex_unit tests as defs
Browse files Browse the repository at this point in the history
register variables defined in test context parameter
fixes missing variable suggestions in tests
  • Loading branch information
lukaszsamson committed Dec 27, 2023
1 parent 83e6e89 commit a7fbd37
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 3 deletions.
92 changes: 92 additions & 0 deletions lib/elixir_sense/core/metadata_builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,80 @@ defmodule ElixirSense.Core.MetadataBuilder do
{[], state}
end

# ex_unit describe
defp pre(
{:describe, meta, [name, _body]} = ast,
state
)
when is_binary(name) do
line = Keyword.fetch!(meta, :line)
column = Keyword.fetch!(meta, :column)

state =
state
|> add_call_to_line({nil, :describe, 2}, {line, column})

%{state | context: Map.put(state.context, :ex_unit_describe, name)}
|> result(ast)
end

# ex_unit not implemented test
defp pre(
{:test, meta, [name]},
state
)
when is_binary(name) do
def_name = ex_unit_test_name(state, name)
line = Keyword.fetch!(meta, :line)
column = Keyword.fetch!(meta, :column)

ast_without_params = {:def, meta, [{def_name, add_no_call([]), []}, [], []]}

state =
state
|> add_call_to_line({nil, :test, 0}, {line, column})

pre_func(ast_without_params, state, meta, def_name, [])
end

# ex_unit test without context
defp pre(
{:test, meta, [name, body]},
state
)
when is_binary(name) do
def_name = ex_unit_test_name(state, name)
line = Keyword.fetch!(meta, :line)
column = Keyword.fetch!(meta, :column)

ast_without_params = {:def, meta, [{def_name, add_no_call([]), []}, [], body]}

state =
state
|> add_call_to_line({nil, :test, 2}, {line, column})

pre_func(ast_without_params, state, meta, def_name, [])
end

# ex_unit test with context
defp pre(
{:test, meta, [name, param, body]},
state
)
when is_binary(name) do
def_name = ex_unit_test_name(state, name)
line = Keyword.fetch!(meta, :line)
column = Keyword.fetch!(meta, :column)

ast_without_params = {:def, meta, [{def_name, add_no_call([]), []}, [], body]}

state =
state
|> add_call_to_line({nil, :test, 3}, {line, column})

pre_func(ast_without_params, state, meta, def_name, [param])
end

# function head with guards
defp pre(
{def_name, meta, [{:when, _, [{name, meta2, params}, guards]}, body]},
Expand Down Expand Up @@ -1461,6 +1535,16 @@ defmodule ElixirSense.Core.MetadataBuilder do
post_module(ast, state)
end

# ex_unit describe
defp post(
{:describe, _meta, [name, _body]} = ast,
state
)
when is_binary(name) do
%{state | context: Map.delete(state.context, :ex_unit_describe)}
|> result(ast)
end

defp post({def_name, meta, [{name, _, _params} | _]} = ast, state)
when def_name in @defs and is_atom(name) do
if Keyword.get(meta, :func, false) do
Expand Down Expand Up @@ -2179,4 +2263,12 @@ defmodule ElixirSense.Core.MetadataBuilder do
other
end)
end

defp ex_unit_test_name(state, name) do
case state.context[:ex_unit_describe] do
nil -> "test #{name}"
describe -> "test #{describe} #{name}"
end
|> String.to_atom()
end
end
2 changes: 2 additions & 0 deletions lib/elixir_sense/core/state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ defmodule ElixirSense.Core.State do
generated: boolean,
first_alias_positions: map(),
moduledoc_positions: map(),
context: map(),
# TODO better type
binding_context: list
}
Expand Down Expand Up @@ -84,6 +85,7 @@ defmodule ElixirSense.Core.State do
types: %{},
generated: false,
binding_context: [],
context: %{},
first_alias_positions: %{},
moduledoc_positions: %{}

Expand Down
49 changes: 46 additions & 3 deletions test/elixir_sense/core/metadata_builder_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,46 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
assert [%VarInfo{name: :var}] = state.vars_info_per_scope_id[scope_id]
end

test "variables are added to environment in ex_unit test" do
state =
"""
defmodule MyModuleTests do
use ExUnit.Case, async: true
test "it does what I want", %{some: some} do
IO.puts("")
end
describe "this" do
test "too does what I want" do
IO.puts("")
end
end
test "is not implemented"
end
"""
|> string_to_state

assert [%VarInfo{type: nil, scope_id: scope_id}] = state |> get_line_vars(5)
assert [%VarInfo{name: :some}] = state.vars_info_per_scope_id[scope_id]

assert Map.has_key?(
state.mods_funs_to_positions,
{MyModuleTests, :"test it does what I want", 1}
)

assert Map.has_key?(
state.mods_funs_to_positions,
{MyModuleTests, :"test this too does what I want", 0}
)

assert Map.has_key?(
state.mods_funs_to_positions,
{MyModuleTests, :"test is not implemented", 0}
)
end

test "variables from outside module are added to environment" do
state =
"""
Expand Down Expand Up @@ -4757,8 +4797,10 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
end
end
test "test2" do
test "test2", %{some: param} do
end
test "not implemented"
end
"""
|> string_to_state
Expand All @@ -4770,7 +4812,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
],
4 => [%CallInfo{arity: 2, position: {4, 3}, func: :describe, mod: nil}],
5 => [%CallInfo{arity: 2, position: {5, 5}, func: :test, mod: nil}],
9 => [%CallInfo{arity: 2, position: {9, 3}, func: :test, mod: nil}]
9 => [%CallInfo{arity: 3, position: {9, 3}, func: :test, mod: nil}],
12 => [%CallInfo{arity: 0, position: {12, 3}, func: :test, mod: nil}]
}
end

Expand Down Expand Up @@ -4873,7 +4916,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
}
end

test "preferes type over typep for nil arity" do
test "prefers type over typep for nil arity" do
state =
"""
defmodule My do
Expand Down

0 comments on commit a7fbd37

Please sign in to comment.