Skip to content

Commit

Permalink
Fix tests and format
Browse files Browse the repository at this point in the history
  • Loading branch information
gcauchon committed Sep 7, 2023
1 parent f16d732 commit f279893
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule ElixirBoilerplateWeb.Controllers.ErrorHTML do
defmodule ElixirBoilerplateWeb.Errors.ErrorHTML do
use ElixirBoilerplateWeb, :html

# If you want to customize your error pages,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule ElixirBoilerplateWeb.Controllers.ErrorJSON do
defmodule ElixirBoilerplateWeb.Errors.ErrorJSON do
# If you want to customize a particular status code,
# you may add your own clauses, such as:
#
Expand Down
68 changes: 68 additions & 0 deletions lib/elixir_boilerplate_web/errors/errors.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
defmodule ElixirBoilerplateWeb.Errors do
alias Ecto.Changeset

import Phoenix.Template, only: [embed_templates: 1]

embed_templates("templates/*")

@doc """
Generates a human-readable block containing all errors in a changeset. Errors
are then localized using translations in the `ecto` domain.
For example, you could have an `errors.po` file in the french locale:
```
msgid ""
msgstr ""
"Language: fr"
msgid "can't be blank"
msgstr "ne peut être vide"
```
"""
def changeset_to_error_messages(changeset) do
changeset
|> Changeset.traverse_errors(&translate_error/1)
|> convert_errors_to_html(changeset.data.__struct__)
end

defp translate_error({message, options}) do
if options[:count] do
Gettext.dngettext(ElixirBoilerplate.Gettext, "errors", message, message, options[:count], options)
else
Gettext.dgettext(ElixirBoilerplate.Gettext, "errors", message, options)
end
end

defp convert_errors_to_html(errors, schema) do
errors = Enum.reduce(errors, [], &convert_error_field(&1, &2, schema))

error_messages(%{errors: errors})
end

defp convert_error_field({field, errors}, memo, schema) when is_list(errors) do
memo ++ Enum.flat_map(errors, &convert_error_subfield(&1, field, [], schema))
end

defp convert_error_field({field, errors}, memo, schema) when is_map(errors) do
memo ++ Enum.flat_map(Map.keys(errors), &convert_error_subfield(&1, field, errors[&1], schema))
end

defp convert_error_subfield(message, field, _, _schema) when is_binary(message) do
# NOTE `schema` is available here if we want to use something like
# `schema.humanize_field(field)` to be able to display `"Email address is
# invalid"` instead of `email is invalid"`.
["#{field} #{message}"]
end

defp convert_error_subfield(message, field, memo, schema) when is_map(message) do
Enum.reduce(message, memo, fn {subfield, errors}, memo ->
memo ++ convert_error_field({"#{field}.#{subfield}", errors}, memo, schema)
end)
end

defp convert_error_subfield(subfield, field, errors, schema) do
field = "#{field}.#{subfield}"
convert_error_field({field, errors}, [], schema)
end
end
11 changes: 11 additions & 0 deletions lib/elixir_boilerplate_web/errors/templates/404.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Not found</title>
</head>
<body>
<p>Sorry, the page you are looking for does not exist.</p>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<%= if @errors != [] do %>
<ul>
<%= for error <- @errors do %>
<li><%= error %></li>
<% end %>
</ul>
<% end %>
16 changes: 16 additions & 0 deletions test/elixir_boilerplate_web/errors/error_html_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
defmodule ElixirBoilerplateWeb.ErrorHtmlTest do
use ElixirBoilerplate.DataCase, async: true

# Bring render_to_string/3 for testing custom views
import Phoenix.Template

alias ElixirBoilerplateWeb.Errors.ErrorHTML

test "renders 404.html" do
assert render_to_string(ErrorHTML, "404", "html", []) == "Not Found"
end

test "renders 500.html" do
assert render_to_string(ErrorHTML, "500", "html", []) == "Internal Server Error"
end
end
13 changes: 13 additions & 0 deletions test/elixir_boilerplate_web/errors/error_json_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule ElixirBoilerplateWeb.ErrorJsonTest do
use ElixirBoilerplate.DataCase, async: true

alias ElixirBoilerplateWeb.Errors.ErrorJSON

test "renders 404" do
assert ErrorJSON.render("404.json", %{}) == %{errors: %{detail: "Not Found"}}
end

test "renders 500" do
assert ErrorJSON.render("500.json", %{}) == %{errors: %{detail: "Internal Server Error"}}
end
end
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
defmodule ElixirBoilerplateWeb.ErrorsTest do
use ElixirBoilerplate.DataCase, async: true

alias ElixirBoilerplateWeb.Errors

defmodule UserRole do
use Ecto.Schema

Expand Down Expand Up @@ -74,7 +72,7 @@ defmodule ElixirBoilerplateWeb.ErrorsTest do

defp changeset_to_error_messages(changeset) do
changeset
|> Errors.changeset_to_error_messages()
|> ElixirBoilerplateWeb.Errors.changeset_to_error_messages()
|> Phoenix.HTML.Safe.to_iodata()
|> IO.iodata_to_binary()
end
Expand Down
2 changes: 0 additions & 2 deletions test/support/conn_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ defmodule ElixirBoilerplateWeb.ConnCase do
import Plug.Conn
import Phoenix.ConnTest

import ElixirBoilerplateWeb.Router.Helpers

# The default endpoint for testing
@endpoint Endpoint
end
Expand Down

0 comments on commit f279893

Please sign in to comment.