Skip to content

Commit

Permalink
enable pretty printing using config
Browse files Browse the repository at this point in the history
  • Loading branch information
dkuku committed Dec 27, 2022
1 parent 8008a80 commit 3a5e480
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 27 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,15 @@ Parameter types and return types are not checked.

Hammox now includes telemetry events! See [Telemetry Guide](https://hexdocs.pm/hammox/Telemetry.html) for more information.

## Configuration

Hammox includes experimental pretty printing of error messages.
To enable it add to `config/test.exs`

```elixir
config :hammox, pretty: true
```

## License

Copyright 2019 Michał Szewczak
Expand Down
62 changes: 37 additions & 25 deletions lib/hammox/type_match_error.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ defmodule Hammox.TypeMatchError do
}
end

defp message_string(reasons) when is_list(reasons) do
reasons
|> Enum.with_index()
|> Enum.map_join("\n\n", fn {reason, index} ->
padding = put_padding(index)

reason
|> human_reason()
|> String.replace_prefix("", padding)
end)
end

defp message_string(reason) when is_tuple(reason) do
message_string([reason])
end

defp human_reason({:arg_type_mismatch, index, value, type}) do
"#{Ordinal.ordinalize(index + 1)} argument value #{custom_inspect(value)} does not match #{Ordinal.ordinalize(index + 1)} parameter's type #{type_to_string(type)}."
end
Expand Down Expand Up @@ -95,22 +111,6 @@ defmodule Hammox.TypeMatchError do
"Value #{custom_inspect(value)} does not implement the #{protocol_name} protocol."
end

defp message_string(reasons) when is_list(reasons) do
reasons
|> Enum.with_index()
|> Enum.map_join("\n\n", fn {reason, index} ->
padding = put_padding(index)

reason
|> human_reason()
|> String.replace_prefix("", padding)
end)
end

defp message_string(reason) when is_tuple(reason) do
message_string([reason])
end

defp type_to_string({:type, _, :map_field_exact, [type1, type2]}) do
"required(#{type_to_string(type1)}) => #{type_to_string(type2)}"
end
Expand All @@ -135,19 +135,27 @@ defmodule Hammox.TypeMatchError do
end

defp format_multiple(type_string) do
padding = "\n" <> get_padding()
if pretty_print() do
padding = "\n" <> get_padding()

type_string
|> String.replace(" | ", padding <> " | ")
|> String.replace_prefix("", padding)
type_string
|> String.replace(" | ", padding <> " | ")
|> String.replace_prefix("", padding)
else
type_string
end
end

defp custom_inspect(value) do
padding = get_padding()

value
|> inspect(limit: :infinity, printable_limit: 500, pretty: true)
|> String.replace("\n", "\n" <> padding)
if pretty_print() do
padding = get_padding()

value
|> inspect(limit: :infinity, printable_limit: 500, pretty: true)
|> String.replace("\n", "\n" <> padding)
else
inspect(value)
end
end

defp put_padding(level) when is_integer(level) do
Expand All @@ -165,4 +173,8 @@ defmodule Hammox.TypeMatchError do
defp get_padding() do
Process.get(:padding)
end

defp pretty_print do
Application.get_env(:hammox, :pretty)
end
end
96 changes: 96 additions & 0 deletions test/hammox/type_match_error_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
defmodule Hammox.TypeMatchErrorTest do
use ExUnit.Case, async: true

alias Hammox.TypeMatchError

describe "standard error" do
test "reason" do
error = TypeMatchError.exception({:error, reason()})

assert error.message ==
"""
Returned value {:ok, %{__struct__: Post, body: "post body", post_body: "nil"}} does not match type {:ok, Post.t()} | {:error, any()}.
Value {:ok, %{__struct__: Post, body: "post body", post_body: "nil"}} does not match type {:ok, Post.t()} | {:error, any()}.
1st tuple element :ok does not match 1st element type :error.
Value :ok does not match type :error.
"""
|> String.replace_trailing("\n", "")
end
end

describe "pretty error" do
setup do
Application.put_env(:hammox, :pretty, true)

on_exit(fn ->
Application.delete_env(:hammox, :pretty)
end)
end

test "reason" do
error = TypeMatchError.exception({:error, reason()})

assert error.message ==
"""
Returned value {:ok, %{__struct__: Post, body: \"post body\", post_body: \"nil\"}} does not match type
{:ok, Post.t()}
| {:error, any()}.
Value {:ok, %{__struct__: Post, body: \"post body\", post_body: \"nil\"}} does not match type
{:ok, Post.t()}
| {:error, any()}.
1st tuple element :ok does not match 1st element type
:error.
Value :ok does not match type
:error.
"""
|> String.replace_trailing("\n", "")
end
end

defp reason do
[
{:return_type_mismatch,
{:ok,
%{
__struct__: Post,
body: "post body",
post_body: "nil"
}},
{:type, 49, :union,
[
{:type, 0, :tuple,
[
{:atom, 0, :ok},
{:remote_type, 49, [{:atom, 0, Post}, {:atom, 0, :t}, []]}
]},
{:type, 0, :tuple, [{:atom, 0, :error}, {:type, 49, :any, []}]}
]}},
{:type_mismatch,
{:ok,
%{
__struct__: Post,
body: "post body",
post_body: "nil"
}},
{:type, 49, :union,
[
{:type, 0, :tuple,
[
{:atom, 0, :ok},
{:remote_type, 49, [{:atom, 0, Post}, {:atom, 0, :t}, []]}
]},
{:type, 0, :tuple, [{:atom, 0, :error}, {:type, 49, :any, []}]}
]}},
{:tuple_elem_type_mismatch, 0, :ok, {:atom, 0, :error}},
{:type_mismatch, :ok, {:atom, 0, :error}}
]
end
end
2 changes: 1 addition & 1 deletion test/hammox_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,7 @@ defmodule HammoxTest do

assert_raise(
Hammox.TypeMatchError,
~r/1st argument value "bar" does not match 1st parameter's type \natom()./,
~r/1st argument value "bar" does not match 1st parameter's type atom()./,
fn -> TestMock.foo_unnamed_arg("bar") end
)
end
Expand Down
2 changes: 1 addition & 1 deletion test/support/behaviour.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ defmodule Hammox.Test.Behaviour do
@callback foo_bitstring_size_literal() :: <<_::3>>
@callback foo_bitstring_unit_literal() :: <<_::_*3>>
@callback foo_bitstring_size_unit_literal() :: <<_::2, _::_*3>>
@callback foo_nullary_function_literal() :: (-> :ok)
@callback foo_nullary_function_literal() :: (() -> :ok)
@callback foo_binary_function_literal() :: (:a, :b -> :ok)
@callback foo_any_arity_function_literal() :: (... -> :ok)
@callback foo_integer_literal() :: 1
Expand Down

0 comments on commit 3a5e480

Please sign in to comment.