+ """
+ end
+
+ ## JS Commands
+
+ def show(js \\ %JS{}, selector) do
+ JS.show(js,
+ to: selector,
+ transition:
+ {"transition-all transform ease-out duration-300",
+ "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95",
+ "opacity-100 translate-y-0 sm:scale-100"}
+ )
+ end
+
+ def hide(js \\ %JS{}, selector) do
+ JS.hide(js,
+ to: selector,
+ time: 200,
+ transition:
+ {"transition-all transform ease-in duration-200",
+ "opacity-100 translate-y-0 sm:scale-100",
+ "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"}
+ )
+ end
+
+ def show_modal(js \\ %JS{}, id) when is_binary(id) do
+ js
+ |> JS.show(to: "##{id}")
+ |> JS.show(
+ to: "##{id}-bg",
+ transition: {"transition-all transform ease-out duration-300", "opacity-0", "opacity-100"}
+ )
+ |> show("##{id}-container")
+ |> JS.add_class("overflow-hidden", to: "body")
+ |> JS.focus_first(to: "##{id}-content")
+ end
+
+ def hide_modal(js \\ %JS{}, id) do
+ js
+ |> JS.hide(
+ to: "##{id}-bg",
+ transition: {"transition-all transform ease-in duration-200", "opacity-100", "opacity-0"}
+ )
+ |> hide("##{id}-container")
+ |> JS.hide(to: "##{id}", transition: {"block", "block", "hidden"})
+ |> JS.remove_class("overflow-hidden", to: "body")
+ |> JS.pop_focus()
+ end
+
+ @doc """
+ Translates an error message using gettext.
+ """
+ def translate_error({msg, opts}) do
+ # When using gettext, we typically pass the strings we want
+ # to translate as a static argument:
+ #
+ # # Translate "is invalid" in the "errors" domain
+ # dgettext("errors", "is invalid")
+ #
+ # # Translate the number of files with plural rules
+ # dngettext("errors", "1 file", "%{count} files", count)
+ #
+ # Because the error messages we show in our forms and APIs
+ # are defined inside Ecto, we need to translate them dynamically.
+ # This requires us to call the Gettext module passing our gettext
+ # backend as first argument.
+ #
+ # Note we use the "errors" domain, which means translations
+ # should be written to the errors.po file. The :count option is
+ # set by Ecto and indicates we should also apply plural rules.
+ if count = opts[:count] do
+ Gettext.dngettext(SomethingErlangWeb.Gettext, "errors", msg, msg, count, opts)
+ else
+ Gettext.dgettext(SomethingErlangWeb.Gettext, "errors", msg, opts)
+ end
+ end
+
+ @doc """
+ Translates the errors for a field from a keyword list of errors.
+ """
+ def translate_errors(errors, field) when is_list(errors) do
+ for {^field, {msg, opts}} <- errors, do: translate_error({msg, opts})
+ end
+
+ defp input_equals?(val1, val2) do
+ Phoenix.HTML.html_escape(val1) == Phoenix.HTML.html_escape(val2)
+ end
+end
diff --git a/lib/something_erlang_web/components/layouts.ex b/lib/something_erlang_web/components/layouts.ex
new file mode 100644
index 0000000..f6cbe48
--- /dev/null
+++ b/lib/something_erlang_web/components/layouts.ex
@@ -0,0 +1,5 @@
+defmodule SomethingErlangWeb.Layouts do
+ use SomethingErlangWeb, :html
+
+ embed_templates "layouts/*"
+end
diff --git a/lib/something_erlang_web/components/layouts/app.html.heex b/lib/something_erlang_web/components/layouts/app.html.heex
new file mode 100644
index 0000000..25c3de6
--- /dev/null
+++ b/lib/something_erlang_web/components/layouts/app.html.heex
@@ -0,0 +1,49 @@
+
+
+ <.link href={~p"/users/log_out"} method="delete">Log out
+
+ <% else %>
+
+ <.link href={~p"/users/register"}>Register
+
+
+ <.link href={~p"/users/log_in"}>Log in
+
+ <% end %>
+
+
+ <%= @inner_content %>
+
+
diff --git a/lib/something_erlang_web/controllers/error_html.ex b/lib/something_erlang_web/controllers/error_html.ex
new file mode 100644
index 0000000..f50105b
--- /dev/null
+++ b/lib/something_erlang_web/controllers/error_html.ex
@@ -0,0 +1,19 @@
+defmodule SomethingErlangWeb.ErrorHTML do
+ use SomethingErlangWeb, :html
+
+ # If you want to customize your error pages,
+ # uncomment the embed_templates/1 call below
+ # and add pages to the error directory:
+ #
+ # * lib/something_erlang_web/controllers/error_html/404.html.heex
+ # * lib/something_erlang_web/controllers/error_html/500.html.heex
+ #
+ # embed_templates "error_html/*"
+
+ # The default is to render a plain text page based on
+ # the template name. For example, "404.html" becomes
+ # "Not Found".
+ def render(template, _assigns) do
+ Phoenix.Controller.status_message_from_template(template)
+ end
+end
diff --git a/lib/something_erlang_web/controllers/error_json.ex b/lib/something_erlang_web/controllers/error_json.ex
new file mode 100644
index 0000000..60ddadc
--- /dev/null
+++ b/lib/something_erlang_web/controllers/error_json.ex
@@ -0,0 +1,15 @@
+defmodule SomethingErlangWeb.ErrorJSON do
+ # If you want to customize a particular status code,
+ # you may add your own clauses, such as:
+ #
+ # def render("500.json", _assigns) do
+ # %{errors: %{detail: "Internal Server Error"}}
+ # end
+
+ # By default, Phoenix returns the status message from
+ # the template name. For example, "404.json" becomes
+ # "Not Found".
+ def render(template, _assigns) do
+ %{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}}
+ end
+end
diff --git a/lib/something_erlang_web/controllers/page_controller.ex b/lib/something_erlang_web/controllers/page_controller.ex
index ed127ed..a6bb25f 100644
--- a/lib/something_erlang_web/controllers/page_controller.ex
+++ b/lib/something_erlang_web/controllers/page_controller.ex
@@ -1,30 +1,9 @@
defmodule SomethingErlangWeb.PageController do
use SomethingErlangWeb, :controller
- def index(conn, _params) do
- render(conn, "index.html")
- end
-
- def to_forum_path(conn, %{"to" => redir_params} = _params) do
- %{"forum_path" => path} = redir_params
-
- {redirect_good, thread, page} =
- case {
- Regex.run(~r{threadid=(\d+)}, path),
- Regex.run(~r{pagenumber=(\d+)}, path)
- } do
- {[_, thread], nil} -> {:ok, thread, 1}
- {[_, thread], [_, page]} -> {:ok, thread, page}
- _ -> {:error, nil, nil}
- end
-
- if redirect_good == :ok do
- redirect(conn,
- to: Routes.thread_show_path(conn, :show, thread, page: page)
- )
- else
- put_flash(conn, :error, "Could not resolve URL")
- render(conn, "index.html")
- end
+ def home(conn, _params) do
+ # The home page is often custom made,
+ # so skip the default app layout.
+ render(conn, :home)
end
end
diff --git a/lib/something_erlang_web/controllers/page_html.ex b/lib/something_erlang_web/controllers/page_html.ex
new file mode 100644
index 0000000..2d01b91
--- /dev/null
+++ b/lib/something_erlang_web/controllers/page_html.ex
@@ -0,0 +1,5 @@
+defmodule SomethingErlangWeb.PageHTML do
+ use SomethingErlangWeb, :html
+
+ embed_templates "page_html/*"
+end
diff --git a/lib/something_erlang_web/controllers/page_html/default.html.heex b/lib/something_erlang_web/controllers/page_html/default.html.heex
new file mode 100644
index 0000000..619f097
--- /dev/null
+++ b/lib/something_erlang_web/controllers/page_html/default.html.heex
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+
+ Phoenix Framework
+
+ v1.7
+
+
+
+ Peace of mind from prototype to production.
+
+
+ Build rich, interactive web applications quickly, with less code and fewer moving parts. Join our growing community of developers using Phoenix to craft APIs, HTML5 apps and more, for fun or at scale.
+
"""
end
@@ -45,10 +27,10 @@ defmodule SomethingErlangWeb.ThreadLive.Show do
def user(assigns) do
~H"""
"""
end
@@ -56,7 +38,8 @@ defmodule SomethingErlangWeb.ThreadLive.Show do
def toolbar(assigns) do
~H"""
+ <.link href={~p"/users/register"}>Register
+ |
+ <.link href={~p"/users/log_in"}>Log in
+
+ """
+ end
+
+ def mount(_params, _session, socket) do
+ {:ok, socket}
+ end
+
+ def handle_event("send_instructions", %{"user" => %{"email" => email}}, socket) do
+ if user = Accounts.get_user_by_email(email) do
+ Accounts.deliver_user_confirmation_instructions(
+ user,
+ &url(~p"/users/confirm/#{&1}")
+ )
+ end
+
+ info =
+ "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
+
+ {:noreply,
+ socket
+ |> put_flash(:info, info)
+ |> redirect(to: ~p"/")}
+ end
+end
diff --git a/lib/something_erlang_web/live/user_confirmation_live.ex b/lib/something_erlang_web/live/user_confirmation_live.ex
new file mode 100644
index 0000000..48aad80
--- /dev/null
+++ b/lib/something_erlang_web/live/user_confirmation_live.ex
@@ -0,0 +1,58 @@
+defmodule SomethingErlangWeb.UserConfirmationLive do
+ use SomethingErlangWeb, :live_view
+
+ alias SomethingErlang.Accounts
+
+ def render(%{live_action: :edit} = assigns) do
+ ~H"""
+
+ <.link href={~p"/users/register"}>Register
+ |
+ <.link href={~p"/users/log_in"}>Log in
+
+
+ """
+ end
+
+ def mount(params, _session, socket) do
+ {:ok, assign(socket, token: params["token"]), temporary_assigns: [token: nil]}
+ end
+
+ # Do not log in the user after confirmation to avoid a
+ # leaked token giving the user access to the account.
+ def handle_event("confirm_account", %{"user" => %{"token" => token}}, socket) do
+ case Accounts.confirm_user(token) do
+ {:ok, _} ->
+ {:noreply,
+ socket
+ |> put_flash(:info, "User confirmed successfully.")
+ |> redirect(to: ~p"/")}
+
+ :error ->
+ # If there is a current user and the account was already confirmed,
+ # then odds are that the confirmation link was already visited, either
+ # by some automation or by the user themselves, so we redirect without
+ # a warning message.
+ case socket.assigns do
+ %{current_user: %{confirmed_at: confirmed_at}} when not is_nil(confirmed_at) ->
+ {:noreply, redirect(socket, to: ~p"/")}
+
+ %{} ->
+ {:noreply,
+ socket
+ |> put_flash(:error, "User confirmation link is invalid or it has expired.")
+ |> redirect(to: ~p"/")}
+ end
+ end
+ end
+end
diff --git a/lib/something_erlang_web/live/user_forgot_password_live.ex b/lib/something_erlang_web/live/user_forgot_password_live.ex
new file mode 100644
index 0000000..0bcda92
--- /dev/null
+++ b/lib/something_erlang_web/live/user_forgot_password_live.ex
@@ -0,0 +1,51 @@
+defmodule SomethingErlangWeb.UserForgotPasswordLive do
+ use SomethingErlangWeb, :live_view
+
+ alias SomethingErlang.Accounts
+
+ def render(assigns) do
+ ~H"""
+
+ <.header class="text-center">
+ Forgot your password?
+ <:subtitle>We'll send a password reset link to your inbox
+
+
+ <.simple_form :let={f} id="reset_password_form" for={:user} phx-submit="send_email">
+ <.input field={{f, :email}} type="email" placeholder="Email" required />
+ <:actions>
+ <.button phx-disable-with="Sending..." class="w-full">
+ Send password reset instructions
+
+
+
+
+ <.link href={~p"/users/register"}>Register
+ |
+ <.link href={~p"/users/log_in"}>Log in
+
+
+ """
+ end
+
+ def mount(_params, _session, socket) do
+ {:ok, socket}
+ end
+
+ def handle_event("send_email", %{"user" => %{"email" => email}}, socket) do
+ if user = Accounts.get_user_by_email(email) do
+ Accounts.deliver_user_reset_password_instructions(
+ user,
+ &url(~p"/users/reset_password/#{&1}")
+ )
+ end
+
+ info =
+ "If your email is in our system, you will receive instructions to reset your password shortly."
+
+ {:noreply,
+ socket
+ |> put_flash(:info, info)
+ |> redirect(to: ~p"/")}
+ end
+end
diff --git a/lib/something_erlang_web/live/user_live_auth.ex b/lib/something_erlang_web/live/user_live_auth.ex
deleted file mode 100644
index 2441600..0000000
--- a/lib/something_erlang_web/live/user_live_auth.ex
+++ /dev/null
@@ -1,16 +0,0 @@
-defmodule SomethingErlangWeb.UserLiveAuth do
- import Phoenix.LiveView
-
- alias SomethingErlang.Accounts
-
- def on_mount(:default, _params, %{"user_token" => user_token} = _session, socket) do
- user = Accounts.get_user_by_session_token(user_token)
- socket = assign_new(socket, :current_user, fn -> user end)
-
- if socket.assigns.current_user.confirmed_at do
- {:cont, socket}
- else
- {:halt, redirect(socket, to: "/login")}
- end
- end
-end
diff --git a/lib/something_erlang_web/live/user_login_live.ex b/lib/something_erlang_web/live/user_login_live.ex
new file mode 100644
index 0000000..74e2d41
--- /dev/null
+++ b/lib/something_erlang_web/live/user_login_live.ex
@@ -0,0 +1,49 @@
+defmodule SomethingErlangWeb.UserLoginLive do
+ use SomethingErlangWeb, :live_view
+
+ def render(assigns) do
+ ~H"""
+
- <%= link "Log in", to: Routes.user_session_path(@conn, :new) %> |
- <%= link "Forgot your password?", to: Routes.user_reset_password_path(@conn, :new) %>
-
- <%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
- <%= link "Forgot your password?", to: Routes.user_reset_password_path(@conn, :new) %>
-
-
diff --git a/lib/something_erlang_web/controllers/user_auth.ex b/lib/something_erlang_web/user_auth.ex
similarity index 59%
rename from lib/something_erlang_web/controllers/user_auth.ex
rename to lib/something_erlang_web/user_auth.ex
index 0362bf6..9b28f04 100644
--- a/lib/something_erlang_web/controllers/user_auth.ex
+++ b/lib/something_erlang_web/user_auth.ex
@@ -1,9 +1,10 @@
defmodule SomethingErlangWeb.UserAuth do
+ use SomethingErlangWeb, :verified_routes
+
import Plug.Conn
import Phoenix.Controller
alias SomethingErlang.Accounts
- alias SomethingErlangWeb.Router.Helpers, as: Routes
# Make the remember me cookie valid for 60 days.
# If you want bump or reduce this value, also change
@@ -30,8 +31,7 @@ defmodule SomethingErlangWeb.UserAuth do
conn
|> renew_session()
- |> put_session(:user_token, token)
- |> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}")
+ |> put_token_in_session(token)
|> maybe_write_remember_me_cookie(token, params)
|> redirect(to: user_return_to || signed_in_path(conn))
end
@@ -72,7 +72,7 @@ defmodule SomethingErlangWeb.UserAuth do
"""
def log_out_user(conn) do
user_token = get_session(conn, :user_token)
- user_token && Accounts.delete_session_token(user_token)
+ user_token && Accounts.delete_user_session_token(user_token)
if live_socket_id = get_session(conn, :live_socket_id) do
SomethingErlangWeb.Endpoint.broadcast(live_socket_id, "disconnect", %{})
@@ -95,19 +95,95 @@ defmodule SomethingErlangWeb.UserAuth do
end
defp ensure_user_token(conn) do
- if user_token = get_session(conn, :user_token) do
- {user_token, conn}
+ if token = get_session(conn, :user_token) do
+ {token, conn}
else
conn = fetch_cookies(conn, signed: [@remember_me_cookie])
- if user_token = conn.cookies[@remember_me_cookie] do
- {user_token, put_session(conn, :user_token, user_token)}
+ if token = conn.cookies[@remember_me_cookie] do
+ {token, put_token_in_session(conn, token)}
else
{nil, conn}
end
end
end
+ @doc """
+ Handles mounting and authenticating the current_user in LiveViews.
+
+ ## `on_mount` arguments
+
+ * `:mount_current_user` - Assigns current_user
+ to socket assigns based on user_token, or nil if
+ there's no user_token or no matching user.
+
+ * `:ensure_authenticated` - Authenticates the user from the session,
+ and assigns the current_user to socket assigns based
+ on user_token.
+ Redirects to login page if there's no logged user.
+
+ * `:redirect_if_user_is_authenticated` - Authenticates the user from the session.
+ Redirects to signed_in_path if there's a logged user.
+
+ ## Examples
+
+ Use the `on_mount` lifecycle macro in LiveViews to mount or authenticate
+ the current_user:
+
+ defmodule SomethingErlangWeb.PageLive do
+ use SomethingErlangWeb, :live_view
+
+ on_mount {SomethingErlangWeb.UserAuth, :mount_current_user}
+ ...
+ end
+
+ Or use the `live_session` of your router to invoke the on_mount callback:
+
+ live_session :authenticated, on_mount: [{SomethingErlangWeb.UserAuth, :ensure_authenticated}] do
+ live "/profile", ProfileLive, :index
+ end
+ """
+ def on_mount(:mount_current_user, _params, session, socket) do
+ {:cont, mount_current_user(session, socket)}
+ end
+
+ def on_mount(:ensure_authenticated, _params, session, socket) do
+ socket = mount_current_user(session, socket)
+
+ if socket.assigns.current_user do
+ {:cont, socket}
+ else
+ socket =
+ socket
+ |> Phoenix.LiveView.put_flash(:error, "You must log in to access this page.")
+ |> Phoenix.LiveView.redirect(to: ~p"/users/log_in")
+
+ {:halt, socket}
+ end
+ end
+
+ def on_mount(:redirect_if_user_is_authenticated, _params, session, socket) do
+ socket = mount_current_user(session, socket)
+
+ if socket.assigns.current_user do
+ {:halt, Phoenix.LiveView.redirect(socket, to: signed_in_path(socket))}
+ else
+ {:cont, socket}
+ end
+ end
+
+ defp mount_current_user(session, socket) do
+ case session do
+ %{"user_token" => user_token} ->
+ Phoenix.Component.assign_new(socket, :current_user, fn ->
+ Accounts.get_user_by_session_token(user_token)
+ end)
+
+ %{} ->
+ Phoenix.Component.assign_new(socket, :current_user, fn -> nil end)
+ end
+ end
+
@doc """
Used for routes that require the user to not be authenticated.
"""
@@ -134,16 +210,22 @@ defmodule SomethingErlangWeb.UserAuth do
conn
|> put_flash(:error, "You must log in to access this page.")
|> maybe_store_return_to()
- |> redirect(to: Routes.user_session_path(conn, :new))
+ |> redirect(to: ~p"/users/log_in")
|> halt()
end
end
+ defp put_token_in_session(conn, token) do
+ conn
+ |> put_session(:user_token, token)
+ |> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}")
+ end
+
defp maybe_store_return_to(%{method: "GET"} = conn) do
put_session(conn, :user_return_to, current_path(conn))
end
defp maybe_store_return_to(conn), do: conn
- defp signed_in_path(_conn), do: "/"
+ defp signed_in_path(_conn), do: ~p"/"
end
diff --git a/lib/something_erlang_web/views/error_helpers.ex b/lib/something_erlang_web/views/error_helpers.ex
deleted file mode 100644
index c6545ef..0000000
--- a/lib/something_erlang_web/views/error_helpers.ex
+++ /dev/null
@@ -1,47 +0,0 @@
-defmodule SomethingErlangWeb.ErrorHelpers do
- @moduledoc """
- Conveniences for translating and building error messages.
- """
-
- use Phoenix.HTML
-
- @doc """
- Generates tag for inlined form input errors.
- """
- def error_tag(form, field) do
- Enum.map(Keyword.get_values(form.errors, field), fn error ->
- content_tag(:span, translate_error(error),
- class: "invalid-feedback",
- phx_feedback_for: input_name(form, field)
- )
- end)
- end
-
- @doc """
- Translates an error message using gettext.
- """
- def translate_error({msg, opts}) do
- # When using gettext, we typically pass the strings we want
- # to translate as a static argument:
- #
- # # Translate "is invalid" in the "errors" domain
- # dgettext("errors", "is invalid")
- #
- # # Translate the number of files with plural rules
- # dngettext("errors", "1 file", "%{count} files", count)
- #
- # Because the error messages we show in our forms and APIs
- # are defined inside Ecto, we need to translate them dynamically.
- # This requires us to call the Gettext module passing our gettext
- # backend as first argument.
- #
- # Note we use the "errors" domain, which means translations
- # should be written to the errors.po file. The :count option is
- # set by Ecto and indicates we should also apply plural rules.
- if count = opts[:count] do
- Gettext.dngettext(SomethingErlangWeb.Gettext, "errors", msg, msg, count, opts)
- else
- Gettext.dgettext(SomethingErlangWeb.Gettext, "errors", msg, opts)
- end
- end
-end
diff --git a/lib/something_erlang_web/views/error_view.ex b/lib/something_erlang_web/views/error_view.ex
deleted file mode 100644
index 6fefd88..0000000
--- a/lib/something_erlang_web/views/error_view.ex
+++ /dev/null
@@ -1,16 +0,0 @@
-defmodule SomethingErlangWeb.ErrorView do
- use SomethingErlangWeb, :view
-
- # If you want to customize a particular status code
- # for a certain format, you may uncomment below.
- # def render("500.html", _assigns) do
- # "Internal Server Error"
- # end
-
- # By default, Phoenix returns the status message from
- # the template name. For example, "404.html" becomes
- # "Not Found".
- def template_not_found(template, _assigns) do
- Phoenix.Controller.status_message_from_template(template)
- end
-end
diff --git a/lib/something_erlang_web/views/layout_view.ex b/lib/something_erlang_web/views/layout_view.ex
deleted file mode 100644
index cbc9499..0000000
--- a/lib/something_erlang_web/views/layout_view.ex
+++ /dev/null
@@ -1,7 +0,0 @@
-defmodule SomethingErlangWeb.LayoutView do
- use SomethingErlangWeb, :view
-
- # Phoenix LiveDashboard is available only in development by default,
- # so we instruct Elixir to not warn if the dashboard route is missing.
- @compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}}
-end
diff --git a/lib/something_erlang_web/views/page_view.ex b/lib/something_erlang_web/views/page_view.ex
deleted file mode 100644
index 11231d3..0000000
--- a/lib/something_erlang_web/views/page_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule SomethingErlangWeb.PageView do
- use SomethingErlangWeb, :view
-end
diff --git a/lib/something_erlang_web/views/user_confirmation_view.ex b/lib/something_erlang_web/views/user_confirmation_view.ex
deleted file mode 100644
index 32ccb1e..0000000
--- a/lib/something_erlang_web/views/user_confirmation_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule SomethingErlangWeb.UserConfirmationView do
- use SomethingErlangWeb, :view
-end
diff --git a/lib/something_erlang_web/views/user_registration_view.ex b/lib/something_erlang_web/views/user_registration_view.ex
deleted file mode 100644
index f294204..0000000
--- a/lib/something_erlang_web/views/user_registration_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule SomethingErlangWeb.UserRegistrationView do
- use SomethingErlangWeb, :view
-end
diff --git a/lib/something_erlang_web/views/user_reset_password_view.ex b/lib/something_erlang_web/views/user_reset_password_view.ex
deleted file mode 100644
index ee1b99d..0000000
--- a/lib/something_erlang_web/views/user_reset_password_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule SomethingErlangWeb.UserResetPasswordView do
- use SomethingErlangWeb, :view
-end
diff --git a/lib/something_erlang_web/views/user_session_view.ex b/lib/something_erlang_web/views/user_session_view.ex
deleted file mode 100644
index 680ff44..0000000
--- a/lib/something_erlang_web/views/user_session_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule SomethingErlangWeb.UserSessionView do
- use SomethingErlangWeb, :view
-end
diff --git a/lib/something_erlang_web/views/user_settings_view.ex b/lib/something_erlang_web/views/user_settings_view.ex
deleted file mode 100644
index f5253a1..0000000
--- a/lib/something_erlang_web/views/user_settings_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule SomethingErlangWeb.UserSettingsView do
- use SomethingErlangWeb, :view
-end
diff --git a/mix.exs b/mix.exs
index 29cbcd2..71d8a3c 100644
--- a/mix.exs
+++ b/mix.exs
@@ -5,9 +5,8 @@ defmodule SomethingErlang.MixProject do
[
app: :something_erlang,
version: "0.1.0",
- elixir: "~> 1.12",
+ elixir: "~> 1.14",
elixirc_paths: elixirc_paths(Mix.env()),
- compilers: [:gettext] ++ Mix.compilers(),
start_permanent: Mix.env() == :prod,
aliases: aliases(),
deps: deps()
@@ -34,25 +33,27 @@ defmodule SomethingErlang.MixProject do
defp deps do
[
{:bcrypt_elixir, "~> 3.0"},
- {:phoenix, "~> 1.6.9"},
+ {:phoenix, "~> 1.7.0-rc.2", override: true},
{:phoenix_ecto, "~> 4.4"},
{:ecto_sql, "~> 3.6"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 3.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
- {:phoenix_live_view, "~> 0.17.5"},
+ {:phoenix_live_view, "~> 0.18.3"},
+ {:heroicons, "~> 0.5"},
{:floki, ">= 0.30.0"},
- {:phoenix_live_dashboard, "~> 0.6"},
- {:esbuild, "~> 0.4", runtime: Mix.env() == :dev},
+ {:phoenix_live_dashboard, "~> 0.7.2"},
+ {:esbuild, "~> 0.5", runtime: Mix.env() == :dev},
+ {:tailwind, "~> 0.1.8", runtime: Mix.env() == :dev},
{:swoosh, "~> 1.3"},
+ {:finch, "~> 0.13"},
{:telemetry_metrics, "~> 0.6"},
{:telemetry_poller, "~> 1.0"},
- {:gettext, "~> 0.18"},
+ {:gettext, "~> 0.20"},
{:jason, "~> 1.2"},
- {:plug_cowboy, "~> 2.5"},
- {:tailwind, "~> 0.1", runtime: Mix.env() == :dev},
+ {:bandit, ">= 0.6.7"},
{:credo, "~> 1.6", only: [:dev, :test], runtime: false},
- {:req, "~> 0.3.0"}
+ {:req, "~> 0.3"}
]
end
@@ -64,15 +65,12 @@ defmodule SomethingErlang.MixProject do
# See the documentation for `Mix` for more info on aliases.
defp aliases do
[
- setup: ["deps.get", "ecto.setup"],
+ setup: ["deps.get", "ecto.setup", "assets.setup"],
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
- "assets.deploy": [
- "tailwind default --minify",
- "esbuild default --minify",
- "phx.digest"
- ]
+ "assets.setup": ["tailwind.install --if-missing", "esbuild.install --if-missing"],
+ "assets.deploy": ["tailwind default --minify", "esbuild default --minify", "phx.digest"]
]
end
end
diff --git a/mix.lock b/mix.lock
index 99d5d77..76a8ec9 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,57 +1,52 @@
%{
+ "bandit": {:hex, :bandit, "0.6.7", "8d768a512ecbda9bd4e71fe223fce8d57c30899ede61dc70a0d4d34407910a8e", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:thousand_island, "~> 0.5.14", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.4.3", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "0f1e9ac0e09714ee54c76001fe9d973aa25bff9cd058668c8f6cd0152f8ca3cf"},
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.0.1", "9be815469e6bfefec40fa74658ecbbe6897acfb57614df1416eeccd4903f602c", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "486bb95efb645d1efc6794c1ddd776a186a9a713abf06f45708a6ce324fb96cf"},
- "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
- "castore": {:hex, :castore, "0.1.17", "ba672681de4e51ed8ec1f74ed624d104c0db72742ea1a5e74edbc770c815182f", [:mix], [], "hexpm", "d9844227ed52d26e7519224525cb6868650c272d4a3d327ce3ca5570c12163f9"},
- "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
- "codepagex": {:hex, :codepagex, "0.1.6", "49110d09a25ee336a983281a48ef883da4c6190481e0b063afe2db481af6117e", [:mix], [], "hexpm", "1521461097dde281edf084062f525a4edc6a5e49f4fd1f5ec41c9c4955d5bd59"},
+ "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
+ "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"},
"comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
- "credo": {:hex, :credo, "1.6.5", "330ca591c12244ab95498d8f47994c493064b2689febf1236d43d596b4f2261d", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "101de53e6907397c3246ccd2cc9b9f0d3fc0b7805b8e1c1c3d818471fc85bafd"},
- "db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"},
+ "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"},
+ "db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
- "ecto": {:hex, :ecto, "3.8.4", "e06b8b87e62b27fea17fd2ff6041572ddd10339fd16cdf58446e402c6c90a74b", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f9244288b8d42db40515463a008cf3f4e0e564bb9c249fe87bf28a6d79fe82d4"},
- "ecto_sql": {:hex, :ecto_sql, "3.8.3", "a7d22c624202546a39d615ed7a6b784580391e65723f2d24f65941b4dd73d471", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.8.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "348cb17fb9e6daf6f251a87049eafcb57805e2892e5e6a0f5dea0985d367329b"},
- "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
- "esbuild": {:hex, :esbuild, "0.5.0", "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5"},
+ "ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"},
+ "ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},
+ "elixir_make": {:hex, :elixir_make, "0.7.3", "c37fdae1b52d2cc51069713a58c2314877c1ad40800a57efb213f77b078a460d", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "24ada3e3996adbed1fa024ca14995ef2ba3d0d17b678b0f3f2b1f66e6ce2b274"},
+ "esbuild": {:hex, :esbuild, "0.6.0", "9ba6ead054abd43cb3d7b14946a0cdd1493698ccd8e054e0e5d6286d7f0f509c", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "30f9a05d4a5bab0d3e37398f312f80864e1ee1a081ca09149d06d474318fd040"},
+ "expo": {:hex, :expo, "0.1.0", "d4e932bdad052c374118e312e35280f1919ac13881cb3ac07a209a54d0c81dd8", [:mix], [], "hexpm", "c22c536021c56de058aaeedeabb4744eb5d48137bacf8c29f04d25b6c6bbbf45"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
- "finch": {:hex, :finch, "0.13.0", "c881e5460ec563bf02d4f4584079e62201db676ed4c0ef3e59189331c4eddf7b", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "49957dcde10dcdc042a123a507a9c5ec5a803f53646d451db2f7dea696fba6cc"},
- "floki": {:hex, :floki, "0.33.1", "f20f1eb471e726342b45ccb68edb9486729e7df94da403936ea94a794f072781", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "461035fd125f13fdf30f243c85a0b1e50afbec876cbf1ceefe6fddd2e6d712c6"},
- "gettext": {:hex, :gettext, "0.19.1", "564953fd21f29358e68b91634799d9d26989f8d039d7512622efb3c3b1c97892", [:mix], [], "hexpm", "10c656c0912b8299adba9b061c06947511e3f109ab0d18b44a866a4498e77222"},
- "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
- "hpax": {:hex, :hpax, "0.1.1", "2396c313683ada39e98c20a75a82911592b47e5c24391363343bde74f82396ca", [:mix], [], "hexpm", "0ae7d5a0b04a8a60caf7a39fcf3ec476f35cc2cc16c05abea730d3ce6ac6c826"},
- "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
- "httpoison": {:hex, :httpoison, "1.8.1", "df030d96de89dad2e9983f92b0c506a642d4b1f4a819c96ff77d12796189c63e", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "35156a6d678d6d516b9229e208942c405cf21232edd632327ecfaf4fd03e79e0"},
- "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
- "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
- "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
- "mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"},
- "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
+ "finch": {:hex, :finch, "0.14.0", "619bfdee18fc135190bf590356c4bf5d5f71f916adb12aec94caa3fa9267a4bc", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5459acaf18c4fdb47a8c22fb3baff5d8173106217c8e56c5ba0b93e66501a8dd"},
+ "floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"},
+ "gettext": {:hex, :gettext, "0.21.0", "15bbceb20b317b706a8041061a08e858b5a189654128618b53746bf36c84352b", [:mix], [{:expo, "~> 0.1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "04a66db4103b6d1d18f92240bb2c73167b517229316b7bef84e4eebbfb2f14f6"},
+ "heroicons": {:hex, :heroicons, "0.5.2", "a7ae72460ecc4b74a4ba9e72f0b5ac3c6897ad08968258597da11c2b0b210683", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.2", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "7ef96f455c1c136c335f1da0f1d7b12c34002c80a224ad96fc0ebf841a6ffef5"},
+ "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
+ "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
+ "mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
"mint": {:hex, :mint, "1.4.2", "50330223429a6e1260b2ca5415f69b0ab086141bc76dc2fbf34d7c389a6675b2", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "ce75a5bbcc59b4d7d8d70f8b2fc284b1751ffb35c7b6a6302b5192f8ab4ddd80"},
- "nimble_options": {:hex, :nimble_options, "0.4.0", "c89babbab52221a24b8d1ff9e7d838be70f0d871be823165c94dd3418eea728f", [:mix], [], "hexpm", "e6701c1af326a11eea9634a3b1c62b475339ace9456c1a23ec3bc9a847bca02d"},
+ "nimble_options": {:hex, :nimble_options, "0.5.2", "42703307b924880f8c08d97719da7472673391905f528259915782bb346e0a1b", [:mix], [], "hexpm", "4da7f904b915fd71db549bcdc25f8d56f378ef7ae07dc1d372cbe72ba950dce0"},
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
- "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
- "phoenix": {:hex, :phoenix, "1.6.11", "29f3c0fd12fa1fc4d4b05e341578e55bc78d96ea83a022587a7e276884d397e4", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1664e34f80c25ea4918fbadd957f491225ef601c0e00b4e644b1a772864bfbc2"},
+ "phoenix": {:hex, :phoenix, "1.7.0-rc.2", "8faaff6f699aad2fe6a003c627da65d0864c868a4c10973ff90abfd7286c1f27", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.4", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "71abde2f67330c55b625dcc0e42bf76662dbadc7553c4f545c2f3759f40f7487"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
"phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
- "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.6.5", "1495bb014be12c9a9252eca04b9af54246f6b5c1e4cd1f30210cd00ec540cf8e", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.3", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.7", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "ef4fa50dd78364409039c99cf6f98ab5209b4c5f8796c17f4db118324f0db852"},
- "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"},
- "phoenix_live_view": {:hex, :phoenix_live_view, "0.17.11", "205f6aa5405648c76f2abcd57716f42fc07d8f21dd8ea7b262dd12b324b50c95", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7177791944b7f90ed18f5935a6a5f07f760b36f7b3bdfb9d28c57440a3c43f99"},
+ "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.2", "97cc4ff2dba1ebe504db72cb45098cb8e91f11160528b980bd282cc45c73b29c", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0e5fdf063c7a3b620c566a30fcf68b7ee02e5e46fe48ee46a6ec3ba382dc05b7"},
+ "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"},
+ "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.9", "476264587c780ccd01a6ba7bae5d8c24e2dbe6eb9e56bc38df884c01ca47012b", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d0dd1444cad2028872eafcbef80d0382c53540265b971afbe671918e0eafe511"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
- "phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"},
- "plug": {:hex, :plug, "1.13.6", "187beb6b67c6cec50503e940f0434ea4692b19384d47e5fdfd701e93cadb4cc2", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02b9c6b9955bce92c829f31d6284bf53c591ca63c4fb9ff81dfd0418667a34ff"},
- "plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"},
- "plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"},
- "postgrex": {:hex, :postgrex, "0.16.3", "fac79a81a9a234b11c44235a4494d8565303fa4b9147acf57e48978a074971db", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "aeaae1d2d1322da4e5fe90d241b0a564ce03a3add09d7270fb85362166194590"},
+ "phoenix_template": {:hex, :phoenix_template, "1.0.0", "c57bc5044f25f007dc86ab21895688c098a9f846a8dda6bc40e2d0ddc146e38f", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "1b066f99a26fd22064c12b2600a9a6e56700f591bf7b20b418054ea38b4d4357"},
+ "plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
+ "plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"},
+ "plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
+ "postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
- "req": {:hex, :req, "0.3.0", "45944bfa0ea21294ad269e2025b9983dd084cc89125c4fc0a8de8a4e7869486b", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.9", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "1212a3e047eede0fa7eeb84c30d08206d44bb120df98b6f6b9a9e04910954a71"},
- "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
- "swoosh": {:hex, :swoosh, "1.7.3", "febb47c8c3ce76747eb9e3ea25ed694c815f72069127e3bb039b7724082ec670", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76abac313f95b6825baa8ceec269d597e8395950c928742fc6451d3456ca256d"},
- "tailwind": {:hex, :tailwind, "0.1.8", "3762defebc8e328fb19ff1afb8c37723e53b52be5ca74f0b8d0a02d1f3f432cf", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "40061d1bf2c0505c6b87be7a3ed05243fc10f6e1af4bac3336db8358bc84d4cc"},
- "telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
+ "req": {:hex, :req, "0.3.4", "a485fd02ea1c5aa24e80ca67e5d66aa9730bad78a6e5cd38345172b50d259ee6", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.9", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "11391a99b9425a2126f7a44340506afd5c3e3e68353d7342546dc3c23c5c514d"},
+ "swoosh": {:hex, :swoosh, "1.9.1", "0a5d7bf9954eb41d7e55525bc0940379982b090abbaef67cd8e1fd2ed7f8ca1a", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76dffff3ffcab80f249d5937a592eaef7cc49ac6f4cdd27e622868326ed6371e"},
+ "tailwind": {:hex, :tailwind, "0.1.9", "25ba09d42f7bfabe170eb67683a76d6ec2061952dc9bd263a52a99ba3d24bd4d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "9213f87709c458aaec313bb5f2df2b4d2cedc2b630e4ae821bf3c54c47a56d0b"},
+ "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
- "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
+ "thousand_island": {:hex, :thousand_island, "0.5.15", "3163c8b61c5e985a80e330d8544c4409e6039a1796587b812385051291b25361", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7347a922f7c8ae3f36737455c6539bba37e3e37c17cde20f9bac3fd0367a52f"},
+ "websock": {:hex, :websock, "0.4.3", "184ac396bdcd3dfceb5b74c17d221af659dd559a95b1b92041ecb51c9b728093", [:mix], [], "hexpm", "5e4dd85f305f43fd3d3e25d70bec4a45228dfed60f0f3b072d8eddff335539cf"},
+ "websock_adapter": {:hex, :websock_adapter, "0.4.5", "30038a3715067f51a9580562c05a3a8d501126030336ffc6edb53bf57d6d2d26", [:mix], [{:bandit, "~> 0.6", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.4", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "1d9812dc7e703c205049426fd4fe0852a247a825f91b099e53dc96f68bafe4c8"},
}
diff --git a/notebooks/client.livemd b/notebooks/client.livemd
deleted file mode 100644
index a534c03..0000000
--- a/notebooks/client.livemd
+++ /dev/null
@@ -1,7 +0,0 @@
-# Client
-
-## Section
-
-```elixir
-SomethingErlangWeb
-```
diff --git a/notebooks/something_erlang.livemd b/notebooks/something_erlang.livemd
deleted file mode 100644
index 2f7e79a..0000000
--- a/notebooks/something_erlang.livemd
+++ /dev/null
@@ -1,80 +0,0 @@
-# Something Erlang
-
-## Intro
-
-It's nice.
-
-## Routes
-
-```elixir
-alias SomethingErlangWeb.Router.Helpers, as: Routes
-```
-
-```elixir
-initial_state = %{
- lv_pid: 123,
- thread_id: 123_456,
- page_number: 1
-}
-
-%{initial_state | page_number: 23}
-```
-
-## Grover's GenServer
-
-```elixir
-DynamicSupervisor.count_children(SomethingErlang.Supervisor.Grovers)
-```
-
-```elixir
-SomethingErlang.Grover.mount(%{bbuserid: 12345, bbpassword: "deadbeaf"})
-```
-
-## Client stuff
-
-```elixir
-defmodule Client do
- def cookies(args) when is_map(args) do
- Enum.map_join(args, ";", fn {k, v} -> "#{k}=#{v}" end)
- end
-end
-
-Client.cookies(%{a: "123", b: "anc"})
-```
-
-```elixir
-SomethingErlang.Accounts.get_user!(1)
-```
-
-```elixir
-user = %{id: "162235", hash: "1542e8ab8b6cf65b766a32220143b97f"}
-SomethingErlang.AwfulApi.parsed_thread(3_898_279, 51, user)
-```
-
-
-
-## Bookmarks
-
-```elixir
-doc = SomethingErlang.AwfulApi.Client.bookmarks_doc(1, user)
-html = Floki.parse_document!(doc)
-
-for td <- Floki.find(html, "tr.thread td") do
- case td do
- {"td", [{"class", <<"icon", _rest::binary>>} | _attrs], _} -> "icon"
- {"td", attrs, _} -> attrs
- end
-end
-```
-
-```elixir
-bookmarks = SomethingErlang.AwfulApi.bookmarks(user)
-```
-
-```elixir
-url = SomethingErlang.AwfulApi.Client.thread_lastseen_page(3_898_279, user)
-```
-
-```elixir
-url = SomethingErlang.AwfulApi.Client.thread_lastseen_page(3_898_279, user)
-```
diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot
index 39a220b..ccf5c68 100644
--- a/priv/gettext/errors.pot
+++ b/priv/gettext/errors.pot
@@ -48,18 +48,18 @@ msgid "are still associated with this entry"
msgstr ""
## From Ecto.Changeset.validate_length/3
-msgid "should be %{count} character(s)"
-msgid_plural "should be %{count} character(s)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "should have %{count} item(s)"
msgid_plural "should have %{count} item(s)"
msgstr[0] ""
msgstr[1] ""
-msgid "should be at least %{count} character(s)"
-msgid_plural "should be at least %{count} character(s)"
+msgid "should be %{count} character(s)"
+msgid_plural "should be %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should be %{count} byte(s)"
+msgid_plural "should be %{count} byte(s)"
msgstr[0] ""
msgstr[1] ""
@@ -68,8 +68,13 @@ msgid_plural "should have at least %{count} item(s)"
msgstr[0] ""
msgstr[1] ""
-msgid "should be at most %{count} character(s)"
-msgid_plural "should be at most %{count} character(s)"
+msgid "should be at least %{count} character(s)"
+msgid_plural "should be at least %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should be at least %{count} byte(s)"
+msgid_plural "should be at least %{count} byte(s)"
msgstr[0] ""
msgstr[1] ""
@@ -78,6 +83,16 @@ msgid_plural "should have at most %{count} item(s)"
msgstr[0] ""
msgstr[1] ""
+msgid "should be at most %{count} character(s)"
+msgid_plural "should be at most %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should be at most %{count} byte(s)"
+msgid_plural "should be at most %{count} byte(s)"
+msgstr[0] ""
+msgstr[1] ""
+
## From Ecto.Changeset.validate_number/3
msgid "must be less than %{number}"
msgstr ""
diff --git a/priv/repo/migrations/20220523092454_create_threads.exs b/priv/repo/migrations/20220523092454_create_threads.exs
deleted file mode 100644
index 08c3d57..0000000
--- a/priv/repo/migrations/20220523092454_create_threads.exs
+++ /dev/null
@@ -1,12 +0,0 @@
-defmodule SomethingErlang.Repo.Migrations.CreateThreads do
- use Ecto.Migration
-
- def change do
- create table(:threads) do
- add :title, :string
- add :thread_id, :integer
-
- timestamps()
- end
- end
-end
diff --git a/priv/repo/migrations/20220718094805_users_add_sadata.exs b/priv/repo/migrations/20220718094805_users_add_sadata.exs
deleted file mode 100644
index ecb6fe0..0000000
--- a/priv/repo/migrations/20220718094805_users_add_sadata.exs
+++ /dev/null
@@ -1,10 +0,0 @@
-defmodule SomethingErlang.Repo.Migrations.UsersAddSadata do
- use Ecto.Migration
-
- def change do
- alter table("users") do
- add :bbuserid, :string
- add :bbpassword, :string
- end
- end
-end
diff --git a/priv/repo/migrations/20220523091744_create_users_auth_tables.exs b/priv/repo/migrations/20230118110156_create_users_auth_tables.exs
similarity index 100%
rename from priv/repo/migrations/20220523091744_create_users_auth_tables.exs
rename to priv/repo/migrations/20230118110156_create_users_auth_tables.exs
diff --git a/priv/static/favicon-a8ca4e3a2bb8fea46a9ee9e102e7d3eb.ico b/priv/static/favicon-a8ca4e3a2bb8fea46a9ee9e102e7d3eb.ico
deleted file mode 100644
index 73de524..0000000
Binary files a/priv/static/favicon-a8ca4e3a2bb8fea46a9ee9e102e7d3eb.ico and /dev/null differ
diff --git a/priv/static/images/phoenix-5bd99a0d17dd41bc9d9bf6840abcc089.png b/priv/static/images/phoenix-5bd99a0d17dd41bc9d9bf6840abcc089.png
deleted file mode 100644
index 9c81075..0000000
Binary files a/priv/static/images/phoenix-5bd99a0d17dd41bc9d9bf6840abcc089.png and /dev/null differ
diff --git a/priv/static/images/phoenix.png b/priv/static/images/phoenix.png
deleted file mode 100644
index 9c81075..0000000
Binary files a/priv/static/images/phoenix.png and /dev/null differ
diff --git a/priv/static/robots-9e2c81b0855bbff2baa8371bc4a78186.txt b/priv/static/robots-9e2c81b0855bbff2baa8371bc4a78186.txt
deleted file mode 100644
index 26e06b5..0000000
--- a/priv/static/robots-9e2c81b0855bbff2baa8371bc4a78186.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
-#
-# To ban all spiders from the entire site uncomment the next two lines:
-# User-agent: *
-# Disallow: /
diff --git a/priv/static/robots-9e2c81b0855bbff2baa8371bc4a78186.txt.gz b/priv/static/robots-9e2c81b0855bbff2baa8371bc4a78186.txt.gz
deleted file mode 100644
index 043be33..0000000
Binary files a/priv/static/robots-9e2c81b0855bbff2baa8371bc4a78186.txt.gz and /dev/null differ
diff --git a/priv/static/robots.txt.gz b/priv/static/robots.txt.gz
deleted file mode 100644
index 043be33..0000000
Binary files a/priv/static/robots.txt.gz and /dev/null differ
diff --git a/test/something_erlang/accounts_test.exs b/test/something_erlang/accounts_test.exs
index 494677c..795be85 100644
--- a/test/something_erlang/accounts_test.exs
+++ b/test/something_erlang/accounts_test.exs
@@ -152,9 +152,9 @@ defmodule SomethingErlang.AccountsTest do
test "validates email uniqueness", %{user: user} do
%{email: email} = user_fixture()
+ password = valid_user_password()
- {:error, changeset} =
- Accounts.apply_user_email(user, valid_user_password(), %{email: email})
+ {:error, changeset} = Accounts.apply_user_email(user, password, %{email: email})
assert "has already been taken" in errors_on(changeset).email
end
@@ -174,7 +174,7 @@ defmodule SomethingErlang.AccountsTest do
end
end
- describe "deliver_update_email_instructions/3" do
+ describe "deliver_user_update_email_instructions/3" do
setup do
%{user: user_fixture()}
end
@@ -182,7 +182,7 @@ defmodule SomethingErlang.AccountsTest do
test "sends token through notification", %{user: user} do
token =
extract_user_token(fn url ->
- Accounts.deliver_update_email_instructions(user, "current@example.com", url)
+ Accounts.deliver_user_update_email_instructions(user, "current@example.com", url)
end)
{:ok, token} = Base.url_decode64(token, padding: false)
@@ -200,7 +200,7 @@ defmodule SomethingErlang.AccountsTest do
token =
extract_user_token(fn url ->
- Accounts.deliver_update_email_instructions(%{user | email: email}, user.email, url)
+ Accounts.deliver_user_update_email_instructions(%{user | email: email}, user.email, url)
end)
%{user: user, token: token, email: email}
@@ -353,11 +353,11 @@ defmodule SomethingErlang.AccountsTest do
end
end
- describe "delete_session_token/1" do
+ describe "delete_user_session_token/1" do
test "deletes the token" do
user = user_fixture()
token = Accounts.generate_user_session_token(user)
- assert Accounts.delete_session_token(token) == :ok
+ assert Accounts.delete_user_session_token(token) == :ok
refute Accounts.get_user_by_session_token(token)
end
end
@@ -500,7 +500,7 @@ defmodule SomethingErlang.AccountsTest do
end
end
- describe "inspect/2" do
+ describe "inspect/2 for the User module" do
test "does not include password" do
refute inspect(%User{password: "123456"}) =~ "password: \"123456\""
end
diff --git a/test/something_erlang/forums_test.exs b/test/something_erlang/forums_test.exs
deleted file mode 100644
index 01b8c8b..0000000
--- a/test/something_erlang/forums_test.exs
+++ /dev/null
@@ -1,61 +0,0 @@
-defmodule SomethingErlang.ForumsTest do
- use SomethingErlang.DataCase
-
- alias SomethingErlang.Forums
-
- describe "threads" do
- alias SomethingErlang.Forums.Thread
-
- import SomethingErlang.ForumsFixtures
-
- @invalid_attrs %{thread_id: nil, title: nil}
-
- test "list_threads/0 returns all threads" do
- thread = thread_fixture()
- assert Forums.list_threads() == [thread]
- end
-
- test "get_thread!/1 returns the thread with given id" do
- thread = thread_fixture()
- assert Forums.get_thread!(thread.id) == thread
- end
-
- test "create_thread/1 with valid data creates a thread" do
- valid_attrs = %{thread_id: 42, title: "some title"}
-
- assert {:ok, %Thread{} = thread} = Forums.create_thread(valid_attrs)
- assert thread.thread_id == 42
- assert thread.title == "some title"
- end
-
- test "create_thread/1 with invalid data returns error changeset" do
- assert {:error, %Ecto.Changeset{}} = Forums.create_thread(@invalid_attrs)
- end
-
- test "update_thread/2 with valid data updates the thread" do
- thread = thread_fixture()
- update_attrs = %{thread_id: 43, title: "some updated title"}
-
- assert {:ok, %Thread{} = thread} = Forums.update_thread(thread, update_attrs)
- assert thread.thread_id == 43
- assert thread.title == "some updated title"
- end
-
- test "update_thread/2 with invalid data returns error changeset" do
- thread = thread_fixture()
- assert {:error, %Ecto.Changeset{}} = Forums.update_thread(thread, @invalid_attrs)
- assert thread == Forums.get_thread!(thread.id)
- end
-
- test "delete_thread/1 deletes the thread" do
- thread = thread_fixture()
- assert {:ok, %Thread{}} = Forums.delete_thread(thread)
- assert_raise Ecto.NoResultsError, fn -> Forums.get_thread!(thread.id) end
- end
-
- test "change_thread/1 returns a thread changeset" do
- thread = thread_fixture()
- assert %Ecto.Changeset{} = Forums.change_thread(thread)
- end
- end
-end
diff --git a/test/something_erlang_web/controllers/error_html_test.exs b/test/something_erlang_web/controllers/error_html_test.exs
new file mode 100644
index 0000000..ba328b8
--- /dev/null
+++ b/test/something_erlang_web/controllers/error_html_test.exs
@@ -0,0 +1,14 @@
+defmodule SomethingErlangWeb.ErrorHTMLTest do
+ use SomethingErlangWeb.ConnCase, async: true
+
+ # Bring render_to_string/4 for testing custom views
+ import Phoenix.Template
+
+ test "renders 404.html" do
+ assert render_to_string(SomethingErlangWeb.ErrorHTML, "404", "html", []) == "Not Found"
+ end
+
+ test "renders 500.html" do
+ assert render_to_string(SomethingErlangWeb.ErrorHTML, "500", "html", []) == "Internal Server Error"
+ end
+end
diff --git a/test/something_erlang_web/controllers/error_json_test.exs b/test/something_erlang_web/controllers/error_json_test.exs
new file mode 100644
index 0000000..23abfe5
--- /dev/null
+++ b/test/something_erlang_web/controllers/error_json_test.exs
@@ -0,0 +1,12 @@
+defmodule SomethingErlangWeb.ErrorJSONTest do
+ use SomethingErlangWeb.ConnCase, async: true
+
+ test "renders 404" do
+ assert SomethingErlangWeb.ErrorJSON.render("404.json", %{}) == %{errors: %{detail: "Not Found"}}
+ end
+
+ test "renders 500" do
+ assert SomethingErlangWeb.ErrorJSON.render("500.json", %{}) ==
+ %{errors: %{detail: "Internal Server Error"}}
+ end
+end
diff --git a/test/something_erlang_web/controllers/page_controller_test.exs b/test/something_erlang_web/controllers/page_controller_test.exs
index 209908a..08e2d89 100644
--- a/test/something_erlang_web/controllers/page_controller_test.exs
+++ b/test/something_erlang_web/controllers/page_controller_test.exs
@@ -2,7 +2,7 @@ defmodule SomethingErlangWeb.PageControllerTest do
use SomethingErlangWeb.ConnCase
test "GET /", %{conn: conn} do
- conn = get(conn, "/")
- assert html_response(conn, 200) =~ "Welcome to Phoenix!"
+ conn = get(conn, ~p"/")
+ assert html_response(conn, 200) =~ "Peace of mind from prototype to production"
end
end
diff --git a/test/something_erlang_web/controllers/user_confirmation_controller_test.exs b/test/something_erlang_web/controllers/user_confirmation_controller_test.exs
deleted file mode 100644
index ebdc5f7..0000000
--- a/test/something_erlang_web/controllers/user_confirmation_controller_test.exs
+++ /dev/null
@@ -1,105 +0,0 @@
-defmodule SomethingErlangWeb.UserConfirmationControllerTest do
- use SomethingErlangWeb.ConnCase, async: true
-
- alias SomethingErlang.Accounts
- alias SomethingErlang.Repo
- import SomethingErlang.AccountsFixtures
-
- setup do
- %{user: user_fixture()}
- end
-
- describe "GET /users/confirm" do
- test "renders the resend confirmation page", %{conn: conn} do
- conn = get(conn, Routes.user_confirmation_path(conn, :new))
- response = html_response(conn, 200)
- assert response =~ "
Resend confirmation instructions
"
- end
- end
-
- describe "POST /users/confirm" do
- @tag :capture_log
- test "sends a new confirmation token", %{conn: conn, user: user} do
- conn =
- post(conn, Routes.user_confirmation_path(conn, :create), %{
- "user" => %{"email" => user.email}
- })
-
- assert redirected_to(conn) == "/"
- assert get_flash(conn, :info) =~ "If your email is in our system"
- assert Repo.get_by!(Accounts.UserToken, user_id: user.id).context == "confirm"
- end
-
- test "does not send confirmation token if User is confirmed", %{conn: conn, user: user} do
- Repo.update!(Accounts.User.confirm_changeset(user))
-
- conn =
- post(conn, Routes.user_confirmation_path(conn, :create), %{
- "user" => %{"email" => user.email}
- })
-
- assert redirected_to(conn) == "/"
- assert get_flash(conn, :info) =~ "If your email is in our system"
- refute Repo.get_by(Accounts.UserToken, user_id: user.id)
- end
-
- test "does not send confirmation token if email is invalid", %{conn: conn} do
- conn =
- post(conn, Routes.user_confirmation_path(conn, :create), %{
- "user" => %{"email" => "unknown@example.com"}
- })
-
- assert redirected_to(conn) == "/"
- assert get_flash(conn, :info) =~ "If your email is in our system"
- assert Repo.all(Accounts.UserToken) == []
- end
- end
-
- describe "GET /users/confirm/:token" do
- test "renders the confirmation page", %{conn: conn} do
- conn = get(conn, Routes.user_confirmation_path(conn, :edit, "some-token"))
- response = html_response(conn, 200)
- assert response =~ "
Confirm account
"
-
- form_action = Routes.user_confirmation_path(conn, :update, "some-token")
- assert response =~ "action=\"#{form_action}\""
- end
- end
-
- describe "POST /users/confirm/:token" do
- test "confirms the given token once", %{conn: conn, user: user} do
- token =
- extract_user_token(fn url ->
- Accounts.deliver_user_confirmation_instructions(user, url)
- end)
-
- conn = post(conn, Routes.user_confirmation_path(conn, :update, token))
- assert redirected_to(conn) == "/"
- assert get_flash(conn, :info) =~ "User confirmed successfully"
- assert Accounts.get_user!(user.id).confirmed_at
- refute get_session(conn, :user_token)
- assert Repo.all(Accounts.UserToken) == []
-
- # When not logged in
- conn = post(conn, Routes.user_confirmation_path(conn, :update, token))
- assert redirected_to(conn) == "/"
- assert get_flash(conn, :error) =~ "User confirmation link is invalid or it has expired"
-
- # When logged in
- conn =
- build_conn()
- |> log_in_user(user)
- |> post(Routes.user_confirmation_path(conn, :update, token))
-
- assert redirected_to(conn) == "/"
- refute get_flash(conn, :error)
- end
-
- test "does not confirm email with invalid token", %{conn: conn, user: user} do
- conn = post(conn, Routes.user_confirmation_path(conn, :update, "oops"))
- assert redirected_to(conn) == "/"
- assert get_flash(conn, :error) =~ "User confirmation link is invalid or it has expired"
- refute Accounts.get_user!(user.id).confirmed_at
- end
- end
-end
diff --git a/test/something_erlang_web/controllers/user_registration_controller_test.exs b/test/something_erlang_web/controllers/user_registration_controller_test.exs
deleted file mode 100644
index 5c5af13..0000000
--- a/test/something_erlang_web/controllers/user_registration_controller_test.exs
+++ /dev/null
@@ -1,54 +0,0 @@
-defmodule SomethingErlangWeb.UserRegistrationControllerTest do
- use SomethingErlangWeb.ConnCase, async: true
-
- import SomethingErlang.AccountsFixtures
-
- describe "GET /users/register" do
- test "renders registration page", %{conn: conn} do
- conn = get(conn, Routes.user_registration_path(conn, :new))
- response = html_response(conn, 200)
- assert response =~ "
Register
"
- assert response =~ "Log in"
- assert response =~ "Register"
- end
-
- test "redirects if already logged in", %{conn: conn} do
- conn = conn |> log_in_user(user_fixture()) |> get(Routes.user_registration_path(conn, :new))
- assert redirected_to(conn) == "/"
- end
- end
-
- describe "POST /users/register" do
- @tag :capture_log
- test "creates account and logs the user in", %{conn: conn} do
- email = unique_user_email()
-
- conn =
- post(conn, Routes.user_registration_path(conn, :create), %{
- "user" => valid_user_attributes(email: email)
- })
-
- assert get_session(conn, :user_token)
- assert redirected_to(conn) == "/"
-
- # Now do a logged in request and assert on the menu
- conn = get(conn, "/")
- response = html_response(conn, 200)
- assert response =~ email
- assert response =~ "Settings"
- assert response =~ "Log out"
- end
-
- test "render errors for invalid data", %{conn: conn} do
- conn =
- post(conn, Routes.user_registration_path(conn, :create), %{
- "user" => %{"email" => "with spaces", "password" => "too short"}
- })
-
- response = html_response(conn, 200)
- assert response =~ "
Register
"
- assert response =~ "must have the @ sign and no spaces"
- assert response =~ "should be at least 12 character"
- end
- end
-end
diff --git a/test/something_erlang_web/controllers/user_reset_password_controller_test.exs b/test/something_erlang_web/controllers/user_reset_password_controller_test.exs
deleted file mode 100644
index 92bf931..0000000
--- a/test/something_erlang_web/controllers/user_reset_password_controller_test.exs
+++ /dev/null
@@ -1,113 +0,0 @@
-defmodule SomethingErlangWeb.UserResetPasswordControllerTest do
- use SomethingErlangWeb.ConnCase, async: true
-
- alias SomethingErlang.Accounts
- alias SomethingErlang.Repo
- import SomethingErlang.AccountsFixtures
-
- setup do
- %{user: user_fixture()}
- end
-
- describe "GET /users/reset_password" do
- test "renders the reset password page", %{conn: conn} do
- conn = get(conn, Routes.user_reset_password_path(conn, :new))
- response = html_response(conn, 200)
- assert response =~ "
Forgot your password?
"
- end
- end
-
- describe "POST /users/reset_password" do
- @tag :capture_log
- test "sends a new reset password token", %{conn: conn, user: user} do
- conn =
- post(conn, Routes.user_reset_password_path(conn, :create), %{
- "user" => %{"email" => user.email}
- })
-
- assert redirected_to(conn) == "/"
- assert get_flash(conn, :info) =~ "If your email is in our system"
- assert Repo.get_by!(Accounts.UserToken, user_id: user.id).context == "reset_password"
- end
-
- test "does not send reset password token if email is invalid", %{conn: conn} do
- conn =
- post(conn, Routes.user_reset_password_path(conn, :create), %{
- "user" => %{"email" => "unknown@example.com"}
- })
-
- assert redirected_to(conn) == "/"
- assert get_flash(conn, :info) =~ "If your email is in our system"
- assert Repo.all(Accounts.UserToken) == []
- end
- end
-
- describe "GET /users/reset_password/:token" do
- setup %{user: user} do
- token =
- extract_user_token(fn url ->
- Accounts.deliver_user_reset_password_instructions(user, url)
- end)
-
- %{token: token}
- end
-
- test "renders reset password", %{conn: conn, token: token} do
- conn = get(conn, Routes.user_reset_password_path(conn, :edit, token))
- assert html_response(conn, 200) =~ "
Reset password
"
- end
-
- test "does not render reset password with invalid token", %{conn: conn} do
- conn = get(conn, Routes.user_reset_password_path(conn, :edit, "oops"))
- assert redirected_to(conn) == "/"
- assert get_flash(conn, :error) =~ "Reset password link is invalid or it has expired"
- end
- end
-
- describe "PUT /users/reset_password/:token" do
- setup %{user: user} do
- token =
- extract_user_token(fn url ->
- Accounts.deliver_user_reset_password_instructions(user, url)
- end)
-
- %{token: token}
- end
-
- test "resets password once", %{conn: conn, user: user, token: token} do
- conn =
- put(conn, Routes.user_reset_password_path(conn, :update, token), %{
- "user" => %{
- "password" => "new valid password",
- "password_confirmation" => "new valid password"
- }
- })
-
- assert redirected_to(conn) == Routes.user_session_path(conn, :new)
- refute get_session(conn, :user_token)
- assert get_flash(conn, :info) =~ "Password reset successfully"
- assert Accounts.get_user_by_email_and_password(user.email, "new valid password")
- end
-
- test "does not reset password on invalid data", %{conn: conn, token: token} do
- conn =
- put(conn, Routes.user_reset_password_path(conn, :update, token), %{
- "user" => %{
- "password" => "too short",
- "password_confirmation" => "does not match"
- }
- })
-
- response = html_response(conn, 200)
- assert response =~ "
Reset password
"
- assert response =~ "should be at least 12 character(s)"
- assert response =~ "does not match password"
- end
-
- test "does not reset password with invalid token", %{conn: conn} do
- conn = put(conn, Routes.user_reset_password_path(conn, :update, "oops"))
- assert redirected_to(conn) == "/"
- assert get_flash(conn, :error) =~ "Reset password link is invalid or it has expired"
- end
- end
-end
diff --git a/test/something_erlang_web/controllers/user_session_controller_test.exs b/test/something_erlang_web/controllers/user_session_controller_test.exs
index 1a0a9f4..bafb576 100644
--- a/test/something_erlang_web/controllers/user_session_controller_test.exs
+++ b/test/something_erlang_web/controllers/user_session_controller_test.exs
@@ -7,33 +7,18 @@ defmodule SomethingErlangWeb.UserSessionControllerTest do
%{user: user_fixture()}
end
- describe "GET /users/log_in" do
- test "renders log in page", %{conn: conn} do
- conn = get(conn, Routes.user_session_path(conn, :new))
- response = html_response(conn, 200)
- assert response =~ "
Log in
"
- assert response =~ "Register"
- assert response =~ "Forgot your password?"
- end
-
- test "redirects if already logged in", %{conn: conn, user: user} do
- conn = conn |> log_in_user(user) |> get(Routes.user_session_path(conn, :new))
- assert redirected_to(conn) == "/"
- end
- end
-
describe "POST /users/log_in" do
test "logs the user in", %{conn: conn, user: user} do
conn =
- post(conn, Routes.user_session_path(conn, :create), %{
+ post(conn, ~p"/users/log_in", %{
"user" => %{"email" => user.email, "password" => valid_user_password()}
})
assert get_session(conn, :user_token)
- assert redirected_to(conn) == "/"
+ assert redirected_to(conn) == ~p"/"
# Now do a logged in request and assert on the menu
- conn = get(conn, "/")
+ conn = get(conn, ~p"/")
response = html_response(conn, 200)
assert response =~ user.email
assert response =~ "Settings"
@@ -42,7 +27,7 @@ defmodule SomethingErlangWeb.UserSessionControllerTest do
test "logs the user in with remember me", %{conn: conn, user: user} do
conn =
- post(conn, Routes.user_session_path(conn, :create), %{
+ post(conn, ~p"/users/log_in", %{
"user" => %{
"email" => user.email,
"password" => valid_user_password(),
@@ -51,14 +36,14 @@ defmodule SomethingErlangWeb.UserSessionControllerTest do
})
assert conn.resp_cookies["_something_erlang_web_user_remember_me"]
- assert redirected_to(conn) == "/"
+ assert redirected_to(conn) == ~p"/"
end
test "logs the user in with return to", %{conn: conn, user: user} do
conn =
conn
|> init_test_session(user_return_to: "/foo/bar")
- |> post(Routes.user_session_path(conn, :create), %{
+ |> post(~p"/users/log_in", %{
"user" => %{
"email" => user.email,
"password" => valid_user_password()
@@ -66,33 +51,63 @@ defmodule SomethingErlangWeb.UserSessionControllerTest do
})
assert redirected_to(conn) == "/foo/bar"
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Welcome back!"
end
- test "emits error message with invalid credentials", %{conn: conn, user: user} do
+ test "login following registration", %{conn: conn, user: user} do
conn =
- post(conn, Routes.user_session_path(conn, :create), %{
- "user" => %{"email" => user.email, "password" => "invalid_password"}
+ conn
+ |> post(~p"/users/log_in", %{
+ "_action" => "registered",
+ "user" => %{
+ "email" => user.email,
+ "password" => valid_user_password()
+ }
})
- response = html_response(conn, 200)
- assert response =~ "
Log in
"
- assert response =~ "Invalid email or password"
+ assert redirected_to(conn) == ~p"/"
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Account created successfully"
+ end
+
+ test "login following password update", %{conn: conn, user: user} do
+ conn =
+ conn
+ |> post(~p"/users/log_in", %{
+ "_action" => "password_updated",
+ "user" => %{
+ "email" => user.email,
+ "password" => valid_user_password()
+ }
+ })
+
+ assert redirected_to(conn) == ~p"/users/settings"
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Password updated successfully"
+ end
+
+ test "redirects to login page with invalid credentials", %{conn: conn} do
+ conn =
+ post(conn, ~p"/users/log_in", %{
+ "user" => %{"email" => "invalid@email.com", "password" => "invalid_password"}
+ })
+
+ assert Phoenix.Flash.get(conn.assigns.flash, :error) == "Invalid email or password"
+ assert redirected_to(conn) == ~p"/users/log_in"
end
end
describe "DELETE /users/log_out" do
test "logs the user out", %{conn: conn, user: user} do
- conn = conn |> log_in_user(user) |> delete(Routes.user_session_path(conn, :delete))
- assert redirected_to(conn) == "/"
+ conn = conn |> log_in_user(user) |> delete(~p"/users/log_out")
+ assert redirected_to(conn) == ~p"/"
refute get_session(conn, :user_token)
- assert get_flash(conn, :info) =~ "Logged out successfully"
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Logged out successfully"
end
test "succeeds even if the user is not logged in", %{conn: conn} do
- conn = delete(conn, Routes.user_session_path(conn, :delete))
- assert redirected_to(conn) == "/"
+ conn = delete(conn, ~p"/users/log_out")
+ assert redirected_to(conn) == ~p"/"
refute get_session(conn, :user_token)
- assert get_flash(conn, :info) =~ "Logged out successfully"
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Logged out successfully"
end
end
end
diff --git a/test/something_erlang_web/controllers/user_settings_controller_test.exs b/test/something_erlang_web/controllers/user_settings_controller_test.exs
deleted file mode 100644
index 853d08d..0000000
--- a/test/something_erlang_web/controllers/user_settings_controller_test.exs
+++ /dev/null
@@ -1,129 +0,0 @@
-defmodule SomethingErlangWeb.UserSettingsControllerTest do
- use SomethingErlangWeb.ConnCase, async: true
-
- alias SomethingErlang.Accounts
- import SomethingErlang.AccountsFixtures
-
- setup :register_and_log_in_user
-
- describe "GET /users/settings" do
- test "renders settings page", %{conn: conn} do
- conn = get(conn, Routes.user_settings_path(conn, :edit))
- response = html_response(conn, 200)
- assert response =~ "
Settings
"
- end
-
- test "redirects if user is not logged in" do
- conn = build_conn()
- conn = get(conn, Routes.user_settings_path(conn, :edit))
- assert redirected_to(conn) == Routes.user_session_path(conn, :new)
- end
- end
-
- describe "PUT /users/settings (change password form)" do
- test "updates the user password and resets tokens", %{conn: conn, user: user} do
- new_password_conn =
- put(conn, Routes.user_settings_path(conn, :update), %{
- "action" => "update_password",
- "current_password" => valid_user_password(),
- "user" => %{
- "password" => "new valid password",
- "password_confirmation" => "new valid password"
- }
- })
-
- assert redirected_to(new_password_conn) == Routes.user_settings_path(conn, :edit)
- assert get_session(new_password_conn, :user_token) != get_session(conn, :user_token)
- assert get_flash(new_password_conn, :info) =~ "Password updated successfully"
- assert Accounts.get_user_by_email_and_password(user.email, "new valid password")
- end
-
- test "does not update password on invalid data", %{conn: conn} do
- old_password_conn =
- put(conn, Routes.user_settings_path(conn, :update), %{
- "action" => "update_password",
- "current_password" => "invalid",
- "user" => %{
- "password" => "too short",
- "password_confirmation" => "does not match"
- }
- })
-
- response = html_response(old_password_conn, 200)
- assert response =~ "
Settings
"
- assert response =~ "should be at least 12 character(s)"
- assert response =~ "does not match password"
- assert response =~ "is not valid"
-
- assert get_session(old_password_conn, :user_token) == get_session(conn, :user_token)
- end
- end
-
- describe "PUT /users/settings (change email form)" do
- @tag :capture_log
- test "updates the user email", %{conn: conn, user: user} do
- conn =
- put(conn, Routes.user_settings_path(conn, :update), %{
- "action" => "update_email",
- "current_password" => valid_user_password(),
- "user" => %{"email" => unique_user_email()}
- })
-
- assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
- assert get_flash(conn, :info) =~ "A link to confirm your email"
- assert Accounts.get_user_by_email(user.email)
- end
-
- test "does not update email on invalid data", %{conn: conn} do
- conn =
- put(conn, Routes.user_settings_path(conn, :update), %{
- "action" => "update_email",
- "current_password" => "invalid",
- "user" => %{"email" => "with spaces"}
- })
-
- response = html_response(conn, 200)
- assert response =~ "
Settings
"
- assert response =~ "must have the @ sign and no spaces"
- assert response =~ "is not valid"
- end
- end
-
- describe "GET /users/settings/confirm_email/:token" do
- setup %{user: user} do
- email = unique_user_email()
-
- token =
- extract_user_token(fn url ->
- Accounts.deliver_update_email_instructions(%{user | email: email}, user.email, url)
- end)
-
- %{token: token, email: email}
- end
-
- test "updates the user email once", %{conn: conn, user: user, token: token, email: email} do
- conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token))
- assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
- assert get_flash(conn, :info) =~ "Email changed successfully"
- refute Accounts.get_user_by_email(user.email)
- assert Accounts.get_user_by_email(email)
-
- conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token))
- assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
- assert get_flash(conn, :error) =~ "Email change link is invalid or it has expired"
- end
-
- test "does not update email with invalid token", %{conn: conn, user: user} do
- conn = get(conn, Routes.user_settings_path(conn, :confirm_email, "oops"))
- assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
- assert get_flash(conn, :error) =~ "Email change link is invalid or it has expired"
- assert Accounts.get_user_by_email(user.email)
- end
-
- test "redirects if user is not logged in", %{token: token} do
- conn = build_conn()
- conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token))
- assert redirected_to(conn) == Routes.user_session_path(conn, :new)
- end
- end
-end
diff --git a/test/something_erlang_web/live/thread_live_test.exs b/test/something_erlang_web/live/thread_live_test.exs
deleted file mode 100644
index b5ba1ac..0000000
--- a/test/something_erlang_web/live/thread_live_test.exs
+++ /dev/null
@@ -1,110 +0,0 @@
-defmodule SomethingErlangWeb.ThreadLiveTest do
- use SomethingErlangWeb.ConnCase
-
- import Phoenix.LiveViewTest
- import SomethingErlang.ForumsFixtures
-
- @create_attrs %{thread_id: 42, title: "some title"}
- @update_attrs %{thread_id: 43, title: "some updated title"}
- @invalid_attrs %{thread_id: nil, title: nil}
-
- defp create_thread(_) do
- thread = thread_fixture()
- %{thread: thread}
- end
-
- describe "Index" do
- setup [:create_thread]
-
- test "lists all threads", %{conn: conn, thread: thread} do
- {:ok, _index_live, html} = live(conn, Routes.thread_index_path(conn, :index))
-
- assert html =~ "Listing Threads"
- assert html =~ thread.title
- end
-
- test "saves new thread", %{conn: conn} do
- {:ok, index_live, _html} = live(conn, Routes.thread_index_path(conn, :index))
-
- assert index_live |> element("a", "New Thread") |> render_click() =~
- "New Thread"
-
- assert_patch(index_live, Routes.thread_index_path(conn, :new))
-
- assert index_live
- |> form("#thread-form", thread: @invalid_attrs)
- |> render_change() =~ "can't be blank"
-
- {:ok, _, html} =
- index_live
- |> form("#thread-form", thread: @create_attrs)
- |> render_submit()
- |> follow_redirect(conn, Routes.thread_index_path(conn, :index))
-
- assert html =~ "Thread created successfully"
- assert html =~ "some title"
- end
-
- test "updates thread in listing", %{conn: conn, thread: thread} do
- {:ok, index_live, _html} = live(conn, Routes.thread_index_path(conn, :index))
-
- assert index_live |> element("#thread-#{thread.id} a", "Edit") |> render_click() =~
- "Edit Thread"
-
- assert_patch(index_live, Routes.thread_index_path(conn, :edit, thread))
-
- assert index_live
- |> form("#thread-form", thread: @invalid_attrs)
- |> render_change() =~ "can't be blank"
-
- {:ok, _, html} =
- index_live
- |> form("#thread-form", thread: @update_attrs)
- |> render_submit()
- |> follow_redirect(conn, Routes.thread_index_path(conn, :index))
-
- assert html =~ "Thread updated successfully"
- assert html =~ "some updated title"
- end
-
- test "deletes thread in listing", %{conn: conn, thread: thread} do
- {:ok, index_live, _html} = live(conn, Routes.thread_index_path(conn, :index))
-
- assert index_live |> element("#thread-#{thread.id} a", "Delete") |> render_click()
- refute has_element?(index_live, "#thread-#{thread.id}")
- end
- end
-
- describe "Show" do
- setup [:create_thread]
-
- test "displays thread", %{conn: conn, thread: thread} do
- {:ok, _show_live, html} = live(conn, Routes.thread_show_path(conn, :show, thread))
-
- assert html =~ "Show Thread"
- assert html =~ thread.title
- end
-
- test "updates thread within modal", %{conn: conn, thread: thread} do
- {:ok, show_live, _html} = live(conn, Routes.thread_show_path(conn, :show, thread))
-
- assert show_live |> element("a", "Edit") |> render_click() =~
- "Edit Thread"
-
- assert_patch(show_live, Routes.thread_show_path(conn, :edit, thread))
-
- assert show_live
- |> form("#thread-form", thread: @invalid_attrs)
- |> render_change() =~ "can't be blank"
-
- {:ok, _, html} =
- show_live
- |> form("#thread-form", thread: @update_attrs)
- |> render_submit()
- |> follow_redirect(conn, Routes.thread_show_path(conn, :show, thread))
-
- assert html =~ "Thread updated successfully"
- assert html =~ "some updated title"
- end
- end
-end
diff --git a/test/something_erlang_web/live/user_confirmation_instructions_live_test.exs b/test/something_erlang_web/live/user_confirmation_instructions_live_test.exs
new file mode 100644
index 0000000..1b94a2d
--- /dev/null
+++ b/test/something_erlang_web/live/user_confirmation_instructions_live_test.exs
@@ -0,0 +1,67 @@
+defmodule SomethingErlangWeb.UserConfirmationInstructionsLiveTest do
+ use SomethingErlangWeb.ConnCase
+
+ import Phoenix.LiveViewTest
+ import SomethingErlang.AccountsFixtures
+
+ alias SomethingErlang.Accounts
+ alias SomethingErlang.Repo
+
+ setup do
+ %{user: user_fixture()}
+ end
+
+ describe "Resend confirmation" do
+ test "renders the resend confirmation page", %{conn: conn} do
+ {:ok, _lv, html} = live(conn, ~p"/users/confirm")
+ assert html =~ "Resend confirmation instructions"
+ end
+
+ test "sends a new confirmation token", %{conn: conn, user: user} do
+ {:ok, lv, _html} = live(conn, ~p"/users/confirm")
+
+ {:ok, conn} =
+ lv
+ |> form("#resend_confirmation_form", user: %{email: user.email})
+ |> render_submit()
+ |> follow_redirect(conn, ~p"/")
+
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~
+ "If your email is in our system"
+
+ assert Repo.get_by!(Accounts.UserToken, user_id: user.id).context == "confirm"
+ end
+
+ test "does not send confirmation token if user is confirmed", %{conn: conn, user: user} do
+ Repo.update!(Accounts.User.confirm_changeset(user))
+
+ {:ok, lv, _html} = live(conn, ~p"/users/confirm")
+
+ {:ok, conn} =
+ lv
+ |> form("#resend_confirmation_form", user: %{email: user.email})
+ |> render_submit()
+ |> follow_redirect(conn, ~p"/")
+
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~
+ "If your email is in our system"
+
+ refute Repo.get_by(Accounts.UserToken, user_id: user.id)
+ end
+
+ test "does not send confirmation token if email is invalid", %{conn: conn} do
+ {:ok, lv, _html} = live(conn, ~p"/users/confirm")
+
+ {:ok, conn} =
+ lv
+ |> form("#resend_confirmation_form", user: %{email: "unknown@example.com"})
+ |> render_submit()
+ |> follow_redirect(conn, ~p"/")
+
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~
+ "If your email is in our system"
+
+ assert Repo.all(Accounts.UserToken) == []
+ end
+ end
+end
diff --git a/test/something_erlang_web/live/user_confirmation_live_test.exs b/test/something_erlang_web/live/user_confirmation_live_test.exs
new file mode 100644
index 0000000..59905a9
--- /dev/null
+++ b/test/something_erlang_web/live/user_confirmation_live_test.exs
@@ -0,0 +1,88 @@
+defmodule SomethingErlangWeb.UserConfirmationLiveTest do
+ use SomethingErlangWeb.ConnCase
+
+ import Phoenix.LiveViewTest
+ import SomethingErlang.AccountsFixtures
+
+ alias SomethingErlang.Accounts
+ alias SomethingErlang.Repo
+
+ setup do
+ %{user: user_fixture()}
+ end
+
+ describe "Confirm user" do
+ test "renders confirmation page", %{conn: conn} do
+ {:ok, _lv, html} = live(conn, ~p"/users/confirm/some-token")
+ assert html =~ "Confirm Account"
+ end
+
+ test "confirms the given token once", %{conn: conn, user: user} do
+ token =
+ extract_user_token(fn url ->
+ Accounts.deliver_user_confirmation_instructions(user, url)
+ end)
+
+ {:ok, lv, _html} = live(conn, ~p"/users/confirm/#{token}")
+
+ result =
+ lv
+ |> form("#confirmation_form")
+ |> render_submit()
+ |> follow_redirect(conn, "/")
+
+ assert {:ok, conn} = result
+
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~
+ "User confirmed successfully"
+
+ assert Accounts.get_user!(user.id).confirmed_at
+ refute get_session(conn, :user_token)
+ assert Repo.all(Accounts.UserToken) == []
+
+ # when not logged in
+ {:ok, lv, _html} = live(conn, ~p"/users/confirm/#{token}")
+
+ result =
+ lv
+ |> form("#confirmation_form")
+ |> render_submit()
+ |> follow_redirect(conn, "/")
+
+ assert {:ok, conn} = result
+
+ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
+ "User confirmation link is invalid or it has expired"
+
+ # when logged in
+ {:ok, lv, _html} =
+ build_conn()
+ |> log_in_user(user)
+ |> live(~p"/users/confirm/#{token}")
+
+ result =
+ lv
+ |> form("#confirmation_form")
+ |> render_submit()
+ |> follow_redirect(conn, "/")
+
+ assert {:ok, conn} = result
+ refute Phoenix.Flash.get(conn.assigns.flash, :error)
+ end
+
+ test "does not confirm email with invalid token", %{conn: conn, user: user} do
+ {:ok, lv, _html} = live(conn, ~p"/users/confirm/invalid-token")
+
+ {:ok, conn} =
+ lv
+ |> form("#confirmation_form")
+ |> render_submit()
+ |> follow_redirect(conn, ~p"/")
+
+ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
+ "User confirmation link is invalid or it has expired"
+
+ refute Accounts.get_user!(user.id).confirmed_at
+ end
+ end
+end
diff --git a/test/something_erlang_web/live/user_forgot_password_live_test.exs b/test/something_erlang_web/live/user_forgot_password_live_test.exs
new file mode 100644
index 0000000..f103f20
--- /dev/null
+++ b/test/something_erlang_web/live/user_forgot_password_live_test.exs
@@ -0,0 +1,63 @@
+defmodule SomethingErlangWeb.UserForgotPasswordLiveTest do
+ use SomethingErlangWeb.ConnCase
+
+ import Phoenix.LiveViewTest
+ import SomethingErlang.AccountsFixtures
+
+ alias SomethingErlang.Accounts
+ alias SomethingErlang.Repo
+
+ describe "Forgot password page" do
+ test "renders email page", %{conn: conn} do
+ {:ok, _lv, html} = live(conn, ~p"/users/reset_password")
+
+ assert html =~ "Forgot your password?"
+ assert html =~ "Register"
+ assert html =~ "Log in"
+ end
+
+ test "redirects if already logged in", %{conn: conn} do
+ result =
+ conn
+ |> log_in_user(user_fixture())
+ |> live(~p"/users/reset_password")
+ |> follow_redirect(conn, ~p"/")
+
+ assert {:ok, _conn} = result
+ end
+ end
+
+ describe "Reset link" do
+ setup do
+ %{user: user_fixture()}
+ end
+
+ test "sends a new reset password token", %{conn: conn, user: user} do
+ {:ok, lv, _html} = live(conn, ~p"/users/reset_password")
+
+ {:ok, conn} =
+ lv
+ |> form("#reset_password_form", user: %{"email" => user.email})
+ |> render_submit()
+ |> follow_redirect(conn, "/")
+
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "If your email is in our system"
+
+ assert Repo.get_by!(Accounts.UserToken, user_id: user.id).context ==
+ "reset_password"
+ end
+
+ test "does not send reset password token if email is invalid", %{conn: conn} do
+ {:ok, lv, _html} = live(conn, ~p"/users/reset_password")
+
+ {:ok, conn} =
+ lv
+ |> form("#reset_password_form", user: %{"email" => "unknown@example.com"})
+ |> render_submit()
+ |> follow_redirect(conn, "/")
+
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "If your email is in our system"
+ assert Repo.all(Accounts.UserToken) == []
+ end
+ end
+end
diff --git a/test/something_erlang_web/live/user_login_live_test.exs b/test/something_erlang_web/live/user_login_live_test.exs
new file mode 100644
index 0000000..9f8add6
--- /dev/null
+++ b/test/something_erlang_web/live/user_login_live_test.exs
@@ -0,0 +1,87 @@
+defmodule SomethingErlangWeb.UserLoginLiveTest do
+ use SomethingErlangWeb.ConnCase
+
+ import Phoenix.LiveViewTest
+ import SomethingErlang.AccountsFixtures
+
+ describe "Log in page" do
+ test "renders log in page", %{conn: conn} do
+ {:ok, _lv, html} = live(conn, ~p"/users/log_in")
+
+ assert html =~ "Log in"
+ assert html =~ "Register"
+ assert html =~ "Forgot your password?"
+ end
+
+ test "redirects if already logged in", %{conn: conn} do
+ result =
+ conn
+ |> log_in_user(user_fixture())
+ |> live(~p"/users/log_in")
+ |> follow_redirect(conn, "/")
+
+ assert {:ok, _conn} = result
+ end
+ end
+
+ describe "user login" do
+ test "redirects if user login with valid credentials", %{conn: conn} do
+ password = "123456789abcd"
+ user = user_fixture(%{password: password})
+
+ {:ok, lv, _html} = live(conn, ~p"/users/log_in")
+
+ form =
+ form(lv, "#login_form", user: %{email: user.email, password: password, remember_me: true})
+
+ conn = submit_form(form, conn)
+
+ assert redirected_to(conn) == ~p"/"
+ end
+
+ test "redirects to login page with a flash error if there are no valid credentials", %{
+ conn: conn
+ } do
+ {:ok, lv, _html} = live(conn, ~p"/users/log_in")
+
+ form =
+ form(lv, "#login_form",
+ user: %{email: "test@email.com", password: "123456", remember_me: true}
+ )
+
+ conn = submit_form(form, conn)
+
+ assert Phoenix.Flash.get(conn.assigns.flash, :error) == "Invalid email or password"
+
+ assert redirected_to(conn) == "/users/log_in"
+ end
+ end
+
+ describe "login navigation" do
+ test "redirects to registration page when the Register button is clicked", %{conn: conn} do
+ {:ok, lv, _html} = live(conn, ~p"/users/log_in")
+
+ {:ok, _login_live, login_html} =
+ lv
+ |> element(~s|a:fl-contains("Sign up")|)
+ |> render_click()
+ |> follow_redirect(conn, ~p"/users/register")
+
+ assert login_html =~ "Register"
+ end
+
+ test "redirects to forgot password page when the Forgot Password button is clicked", %{
+ conn: conn
+ } do
+ {:ok, lv, _html} = live(conn, ~p"/users/log_in")
+
+ {:ok, conn} =
+ lv
+ |> element(~s{a:fl-contains('Forgot your password?')})
+ |> render_click()
+ |> follow_redirect(conn, ~p"/users/reset_password")
+
+ assert conn.resp_body =~ "Forgot your password?"
+ end
+ end
+end
diff --git a/test/something_erlang_web/live/user_registration_live_test.exs b/test/something_erlang_web/live/user_registration_live_test.exs
new file mode 100644
index 0000000..2cdb902
--- /dev/null
+++ b/test/something_erlang_web/live/user_registration_live_test.exs
@@ -0,0 +1,87 @@
+defmodule SomethingErlangWeb.UserRegistrationLiveTest do
+ use SomethingErlangWeb.ConnCase
+
+ import Phoenix.LiveViewTest
+ import SomethingErlang.AccountsFixtures
+
+ describe "Registration page" do
+ test "renders registration page", %{conn: conn} do
+ {:ok, _lv, html} = live(conn, ~p"/users/register")
+
+ assert html =~ "Register"
+ assert html =~ "Log in"
+ end
+
+ test "redirects if already logged in", %{conn: conn} do
+ result =
+ conn
+ |> log_in_user(user_fixture())
+ |> live(~p"/users/register")
+ |> follow_redirect(conn, "/")
+
+ assert {:ok, _conn} = result
+ end
+
+ test "renders errors for invalid data", %{conn: conn} do
+ {:ok, lv, _html} = live(conn, ~p"/users/register")
+
+ result =
+ lv
+ |> element("#registration_form")
+ |> render_change(user: %{"email" => "with spaces", "password" => "too short"})
+
+ assert result =~ "Register"
+ assert result =~ "must have the @ sign and no spaces"
+ assert result =~ "should be at least 12 character"
+ end
+ end
+
+ describe "register user" do
+ test "creates account and logs the user in", %{conn: conn} do
+ {:ok, lv, _html} = live(conn, ~p"/users/register")
+
+ email = unique_user_email()
+ form = form(lv, "#registration_form", user: valid_user_attributes(email: email))
+ render_submit(form)
+ conn = follow_trigger_action(form, conn)
+
+ assert redirected_to(conn) == ~p"/"
+
+ # Now do a logged in request and assert on the menu
+ conn = get(conn, "/")
+ response = html_response(conn, 200)
+ assert response =~ email
+ assert response =~ "Settings"
+ assert response =~ "Log out"
+ end
+
+ test "renders errors for duplicated email", %{conn: conn} do
+ {:ok, lv, _html} = live(conn, ~p"/users/register")
+
+ user = user_fixture(%{email: "test@email.com"})
+
+ result =
+ lv
+ |> form("#registration_form",
+ user: %{"email" => user.email, "password" => "valid_password"}
+ )
+ |> render_submit()
+
+ assert result =~ "has already been taken"
+ end
+ end
+
+ describe "registration navigation" do
+ test "redirects to login page when the Log in button is clicked", %{conn: conn} do
+ {:ok, lv, _html} = live(conn, ~p"/users/register")
+
+ {:ok, _login_live, login_html} =
+ lv
+ |> element(~s|main a:fl-contains("Sign in")|)
+ |> render_click()
+ |> follow_redirect(conn, ~p"/users/log_in")
+
+ assert login_html =~ "Log in"
+ end
+ end
+end
diff --git a/test/something_erlang_web/live/user_reset_password_live_test.exs b/test/something_erlang_web/live/user_reset_password_live_test.exs
new file mode 100644
index 0000000..17fc8bd
--- /dev/null
+++ b/test/something_erlang_web/live/user_reset_password_live_test.exs
@@ -0,0 +1,118 @@
+defmodule SomethingErlangWeb.UserResetPasswordLiveTest do
+ use SomethingErlangWeb.ConnCase
+
+ import Phoenix.LiveViewTest
+ import SomethingErlang.AccountsFixtures
+
+ alias SomethingErlang.Accounts
+
+ setup do
+ user = user_fixture()
+
+ token =
+ extract_user_token(fn url ->
+ Accounts.deliver_user_reset_password_instructions(user, url)
+ end)
+
+ %{token: token, user: user}
+ end
+
+ describe "Reset password page" do
+ test "renders reset password with valid token", %{conn: conn, token: token} do
+ {:ok, _lv, html} = live(conn, ~p"/users/reset_password/#{token}")
+
+ assert html =~ "Reset Password"
+ end
+
+ test "does not render reset password with invalid token", %{conn: conn} do
+ {:error, {:redirect, to}} = live(conn, ~p"/users/reset_password/invalid")
+
+ assert to == %{
+ flash: %{"error" => "Reset password link is invalid or it has expired."},
+ to: ~p"/"
+ }
+ end
+
+ test "renders errors for invalid data", %{conn: conn, token: token} do
+ {:ok, lv, _html} = live(conn, ~p"/users/reset_password/#{token}")
+
+ result =
+ lv
+ |> element("#reset_password_form")
+ |> render_change(
+ user: %{"password" => "secret12", "confirmation_password" => "secret123456"}
+ )
+
+ assert result =~ "should be at least 12 character"
+ assert result =~ "does not match password"
+ end
+ end
+
+ describe "Reset Password" do
+ test "resets password once", %{conn: conn, token: token, user: user} do
+ {:ok, lv, _html} = live(conn, ~p"/users/reset_password/#{token}")
+
+ {:ok, conn} =
+ lv
+ |> form("#reset_password_form",
+ user: %{
+ "password" => "new valid password",
+ "password_confirmation" => "new valid password"
+ }
+ )
+ |> render_submit()
+ |> follow_redirect(conn, ~p"/users/log_in")
+
+ refute get_session(conn, :user_token)
+ assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Password reset successfully"
+ assert Accounts.get_user_by_email_and_password(user.email, "new valid password")
+ end
+
+ test "does not reset password on invalid data", %{conn: conn, token: token} do
+ {:ok, lv, _html} = live(conn, ~p"/users/reset_password/#{token}")
+
+ result =
+ lv
+ |> form("#reset_password_form",
+ user: %{
+ "password" => "too short",
+ "password_confirmation" => "does not match"
+ }
+ )
+ |> render_submit()
+
+ assert result =~ "Reset Password"
+ assert result =~ "should be at least 12 character(s)"
+ assert result =~ "does not match password"
+ end
+ end
+
+ describe "Reset password navigation" do
+ test "redirects to login page when the Log in button is clicked", %{conn: conn, token: token} do
+ {:ok, lv, _html} = live(conn, ~p"/users/reset_password/#{token}")
+
+ {:ok, conn} =
+ lv
+ |> element(~s|main a:fl-contains("Log in")|)
+ |> render_click()
+ |> follow_redirect(conn, ~p"/users/log_in")
+
+ assert conn.resp_body =~ "Log in"
+ end
+
+ test "redirects to password reset page when the Register button is clicked", %{
+ conn: conn,
+ token: token
+ } do
+ {:ok, lv, _html} = live(conn, ~p"/users/reset_password/#{token}")
+
+ {:ok, conn} =
+ lv
+ |> element(~s|main a:fl-contains("Register")|)
+ |> render_click()
+ |> follow_redirect(conn, ~p"/users/register")
+
+ assert conn.resp_body =~ "Register"
+ end
+ end
+end
diff --git a/test/something_erlang_web/live/user_settings_live_test.exs b/test/something_erlang_web/live/user_settings_live_test.exs
new file mode 100644
index 0000000..e095de8
--- /dev/null
+++ b/test/something_erlang_web/live/user_settings_live_test.exs
@@ -0,0 +1,210 @@
+defmodule SomethingErlangWeb.UserSettingsLiveTest do
+ use SomethingErlangWeb.ConnCase
+
+ alias SomethingErlang.Accounts
+ import Phoenix.LiveViewTest
+ import SomethingErlang.AccountsFixtures
+
+ describe "Settings page" do
+ test "renders settings page", %{conn: conn} do
+ {:ok, _lv, html} =
+ conn
+ |> log_in_user(user_fixture())
+ |> live(~p"/users/settings")
+
+ assert html =~ "Change Email"
+ assert html =~ "Change Password"
+ end
+
+ test "redirects if user is not logged in", %{conn: conn} do
+ assert {:error, redirect} = live(conn, ~p"/users/settings")
+
+ assert {:redirect, %{to: path, flash: flash}} = redirect
+ assert path == ~p"/users/log_in"
+ assert %{"error" => "You must log in to access this page."} = flash
+ end
+ end
+
+ describe "update email form" do
+ setup %{conn: conn} do
+ password = valid_user_password()
+ user = user_fixture(%{password: password})
+ %{conn: log_in_user(conn, user), user: user, password: password}
+ end
+
+ test "updates the user email", %{conn: conn, password: password, user: user} do
+ new_email = unique_user_email()
+
+ {:ok, lv, _html} = live(conn, ~p"/users/settings")
+
+ result =
+ lv
+ |> form("#email_form", %{
+ "current_password" => password,
+ "user" => %{"email" => new_email}
+ })
+ |> render_submit()
+
+ assert result =~ "A link to confirm your email"
+ assert Accounts.get_user_by_email(user.email)
+ end
+
+ test "renders errors with invalid data (phx-change)", %{conn: conn} do
+ {:ok, lv, _html} = live(conn, ~p"/users/settings")
+
+ result =
+ lv
+ |> element("#email_form")
+ |> render_change(%{
+ "action" => "update_email",
+ "current_password" => "invalid",
+ "user" => %{"email" => "with spaces"}
+ })
+
+ assert result =~ "Change Email"
+ assert result =~ "must have the @ sign and no spaces"
+ end
+
+ test "renders errors with invalid data (phx-submit)", %{conn: conn, user: user} do
+ {:ok, lv, _html} = live(conn, ~p"/users/settings")
+
+ result =
+ lv
+ |> form("#email_form", %{
+ "current_password" => "invalid",
+ "user" => %{"email" => user.email}
+ })
+ |> render_submit()
+
+ assert result =~ "Change Email"
+ assert result =~ "did not change"
+ assert result =~ "is not valid"
+ end
+ end
+
+ describe "update password form" do
+ setup %{conn: conn} do
+ password = valid_user_password()
+ user = user_fixture(%{password: password})
+ %{conn: log_in_user(conn, user), user: user, password: password}
+ end
+
+ test "updates the user password", %{conn: conn, user: user, password: password} do
+ new_password = valid_user_password()
+
+ {:ok, lv, _html} = live(conn, ~p"/users/settings")
+
+ form =
+ form(lv, "#password_form", %{
+ "current_password" => password,
+ "user" => %{
+ "email" => user.email,
+ "password" => new_password,
+ "password_confirmation" => new_password
+ }
+ })
+
+ render_submit(form)
+
+ new_password_conn = follow_trigger_action(form, conn)
+
+ assert redirected_to(new_password_conn) == ~p"/users/settings"
+
+ assert get_session(new_password_conn, :user_token) != get_session(conn, :user_token)
+
+ assert Phoenix.Flash.get(new_password_conn.assigns.flash, :info) =~
+ "Password updated successfully"
+
+ assert Accounts.get_user_by_email_and_password(user.email, new_password)
+ end
+
+ test "renders errors with invalid data (phx-change)", %{conn: conn} do
+ {:ok, lv, _html} = live(conn, ~p"/users/settings")
+
+ result =
+ lv
+ |> element("#password_form")
+ |> render_change(%{
+ "current_password" => "invalid",
+ "user" => %{
+ "password" => "too short",
+ "password_confirmation" => "does not match"
+ }
+ })
+
+ assert result =~ "Change Password"
+ assert result =~ "should be at least 12 character(s)"
+ assert result =~ "does not match password"
+ end
+
+ test "renders errors with invalid data (phx-submit)", %{conn: conn} do
+ {:ok, lv, _html} = live(conn, ~p"/users/settings")
+
+ result =
+ lv
+ |> form("#password_form", %{
+ "current_password" => "invalid",
+ "user" => %{
+ "password" => "too short",
+ "password_confirmation" => "does not match"
+ }
+ })
+ |> render_submit()
+
+ assert result =~ "Change Password"
+ assert result =~ "should be at least 12 character(s)"
+ assert result =~ "does not match password"
+ assert result =~ "is not valid"
+ end
+ end
+
+ describe "confirm email" do
+ setup %{conn: conn} do
+ user = user_fixture()
+ email = unique_user_email()
+
+ token =
+ extract_user_token(fn url ->
+ Accounts.deliver_user_update_email_instructions(%{user | email: email}, user.email, url)
+ end)
+
+ %{conn: log_in_user(conn, user), token: token, email: email, user: user}
+ end
+
+ test "updates the user email once", %{conn: conn, user: user, token: token, email: email} do
+ {:error, redirect} = live(conn, ~p"/users/settings/confirm_email/#{token}")
+
+ assert {:live_redirect, %{to: path, flash: flash}} = redirect
+ assert path == ~p"/users/settings"
+ assert %{"info" => message} = flash
+ assert message == "Email changed successfully."
+ refute Accounts.get_user_by_email(user.email)
+ assert Accounts.get_user_by_email(email)
+
+ # use confirm token again
+ {:error, redirect} = live(conn, ~p"/users/settings/confirm_email/#{token}")
+ assert {:live_redirect, %{to: path, flash: flash}} = redirect
+ assert path == ~p"/users/settings"
+ assert %{"error" => message} = flash
+ assert message == "Email change link is invalid or it has expired."
+ end
+
+ test "does not update email with invalid token", %{conn: conn, user: user} do
+ {:error, redirect} = live(conn, ~p"/users/settings/confirm_email/oops")
+ assert {:live_redirect, %{to: path, flash: flash}} = redirect
+ assert path == ~p"/users/settings"
+ assert %{"error" => message} = flash
+ assert message == "Email change link is invalid or it has expired."
+ assert Accounts.get_user_by_email(user.email)
+ end
+
+ test "redirects if user is not logged in", %{token: token} do
+ conn = build_conn()
+ {:error, redirect} = live(conn, ~p"/users/settings/confirm_email/#{token}")
+ assert {:redirect, %{to: path, flash: flash}} = redirect
+ assert path == ~p"/users/log_in"
+ assert %{"error" => message} = flash
+ assert message == "You must log in to access this page."
+ end
+ end
+end
diff --git a/test/something_erlang_web/controllers/user_auth_test.exs b/test/something_erlang_web/user_auth_test.exs
similarity index 60%
rename from test/something_erlang_web/controllers/user_auth_test.exs
rename to test/something_erlang_web/user_auth_test.exs
index 80ee8d1..817e0cb 100644
--- a/test/something_erlang_web/controllers/user_auth_test.exs
+++ b/test/something_erlang_web/user_auth_test.exs
@@ -1,6 +1,7 @@
defmodule SomethingErlangWeb.UserAuthTest do
use SomethingErlangWeb.ConnCase, async: true
+ alias Phoenix.LiveView
alias SomethingErlang.Accounts
alias SomethingErlangWeb.UserAuth
import SomethingErlang.AccountsFixtures
@@ -21,7 +22,7 @@ defmodule SomethingErlangWeb.UserAuthTest do
conn = UserAuth.log_in_user(conn, user)
assert token = get_session(conn, :user_token)
assert get_session(conn, :live_socket_id) == "users_sessions:#{Base.url_encode64(token)}"
- assert redirected_to(conn) == "/"
+ assert redirected_to(conn) == ~p"/"
assert Accounts.get_user_by_session_token(token)
end
@@ -59,7 +60,7 @@ defmodule SomethingErlangWeb.UserAuthTest do
refute get_session(conn, :user_token)
refute conn.cookies[@remember_me_cookie]
assert %{max_age: 0} = conn.resp_cookies[@remember_me_cookie]
- assert redirected_to(conn) == "/"
+ assert redirected_to(conn) == ~p"/"
refute Accounts.get_user_by_session_token(user_token)
end
@@ -78,7 +79,7 @@ defmodule SomethingErlangWeb.UserAuthTest do
conn = conn |> fetch_cookies() |> UserAuth.log_out_user()
refute get_session(conn, :user_token)
assert %{max_age: 0} = conn.resp_cookies[@remember_me_cookie]
- assert redirected_to(conn) == "/"
+ assert redirected_to(conn) == ~p"/"
end
end
@@ -101,8 +102,11 @@ defmodule SomethingErlangWeb.UserAuthTest do
|> put_req_cookie(@remember_me_cookie, signed_token)
|> UserAuth.fetch_current_user([])
- assert get_session(conn, :user_token) == user_token
assert conn.assigns.current_user.id == user.id
+ assert get_session(conn, :user_token) == user_token
+
+ assert get_session(conn, :live_socket_id) ==
+ "users_sessions:#{Base.url_encode64(user_token)}"
end
test "does not authenticate if data is missing", %{conn: conn, user: user} do
@@ -113,11 +117,106 @@ defmodule SomethingErlangWeb.UserAuthTest do
end
end
+ describe "on_mount: mount_current_user" do
+ test "assigns current_user based on a valid user_token ", %{conn: conn, user: user} do
+ user_token = Accounts.generate_user_session_token(user)
+ session = conn |> put_session(:user_token, user_token) |> get_session()
+
+ {:cont, updated_socket} =
+ UserAuth.on_mount(:mount_current_user, %{}, session, %LiveView.Socket{})
+
+ assert updated_socket.assigns.current_user.id == user.id
+ end
+
+ test "assigns nil to current_user assign if there isn't a valid user_token ", %{conn: conn} do
+ user_token = "invalid_token"
+ session = conn |> put_session(:user_token, user_token) |> get_session()
+
+ {:cont, updated_socket} =
+ UserAuth.on_mount(:mount_current_user, %{}, session, %LiveView.Socket{})
+
+ assert updated_socket.assigns.current_user == nil
+ end
+
+ test "assigns nil to current_user assign if there isn't a user_token", %{conn: conn} do
+ session = conn |> get_session()
+
+ {:cont, updated_socket} =
+ UserAuth.on_mount(:mount_current_user, %{}, session, %LiveView.Socket{})
+
+ assert updated_socket.assigns.current_user == nil
+ end
+ end
+
+ describe "on_mount: ensure_authenticated" do
+ test "authenticates current_user based on a valid user_token ", %{conn: conn, user: user} do
+ user_token = Accounts.generate_user_session_token(user)
+ session = conn |> put_session(:user_token, user_token) |> get_session()
+
+ {:cont, updated_socket} =
+ UserAuth.on_mount(:ensure_authenticated, %{}, session, %LiveView.Socket{})
+
+ assert updated_socket.assigns.current_user.id == user.id
+ end
+
+ test "redirects to login page if there isn't a valid user_token ", %{conn: conn} do
+ user_token = "invalid_token"
+ session = conn |> put_session(:user_token, user_token) |> get_session()
+
+ socket = %LiveView.Socket{
+ endpoint: SomethingErlangWeb.Endpoint,
+ assigns: %{__changed__: %{}, flash: %{}}
+ }
+
+ {:halt, updated_socket} = UserAuth.on_mount(:ensure_authenticated, %{}, session, socket)
+ assert updated_socket.assigns.current_user == nil
+ end
+
+ test "redirects to login page if there isn't a user_token ", %{conn: conn} do
+ session = conn |> get_session()
+
+ socket = %LiveView.Socket{
+ endpoint: SomethingErlangWeb.Endpoint,
+ assigns: %{__changed__: %{}, flash: %{}}
+ }
+
+ {:halt, updated_socket} = UserAuth.on_mount(:ensure_authenticated, %{}, session, socket)
+ assert updated_socket.assigns.current_user == nil
+ end
+ end
+
+ describe "on_mount: :redirect_if_user_is_authenticated" do
+ test "redirects if there is an authenticated user ", %{conn: conn, user: user} do
+ user_token = Accounts.generate_user_session_token(user)
+ session = conn |> put_session(:user_token, user_token) |> get_session()
+
+ assert {:halt, _updated_socket} =
+ UserAuth.on_mount(
+ :redirect_if_user_is_authenticated,
+ %{},
+ session,
+ %LiveView.Socket{}
+ )
+ end
+
+ test "Don't redirect is there is no authenticated user", %{conn: conn} do
+ session = conn |> get_session()
+
+ assert {:cont, _updated_socket} =
+ UserAuth.on_mount(
+ :redirect_if_user_is_authenticated,
+ %{},
+ session,
+ %LiveView.Socket{}
+ )
+ end
+ end
+
describe "redirect_if_user_is_authenticated/2" do
test "redirects if user is authenticated", %{conn: conn, user: user} do
conn = conn |> assign(:current_user, user) |> UserAuth.redirect_if_user_is_authenticated([])
assert conn.halted
- assert redirected_to(conn) == "/"
+ assert redirected_to(conn) == ~p"/"
end
test "does not redirect if user is not authenticated", %{conn: conn} do
@@ -131,8 +230,11 @@ defmodule SomethingErlangWeb.UserAuthTest do
test "redirects if user is not authenticated", %{conn: conn} do
conn = conn |> fetch_flash() |> UserAuth.require_authenticated_user([])
assert conn.halted
- assert redirected_to(conn) == Routes.user_session_path(conn, :new)
- assert get_flash(conn, :error) == "You must log in to access this page."
+
+ assert redirected_to(conn) == ~p"/users/log_in"
+
+ assert Phoenix.Flash.get(conn.assigns.flash, :error) ==
+ "You must log in to access this page."
end
test "stores the path to redirect to on GET", %{conn: conn} do
diff --git a/test/something_erlang_web/views/error_view_test.exs b/test/something_erlang_web/views/error_view_test.exs
deleted file mode 100644
index 1bfb4b5..0000000
--- a/test/something_erlang_web/views/error_view_test.exs
+++ /dev/null
@@ -1,15 +0,0 @@
-defmodule SomethingErlangWeb.ErrorViewTest do
- use SomethingErlangWeb.ConnCase, async: true
-
- # Bring render/3 and render_to_string/3 for testing custom views
- import Phoenix.View
-
- test "renders 404.html" do
- assert render_to_string(SomethingErlangWeb.ErrorView, "404.html", []) == "Not Found"
- end
-
- test "renders 500.html" do
- assert render_to_string(SomethingErlangWeb.ErrorView, "500.html", []) ==
- "Internal Server Error"
- end
-end
diff --git a/test/something_erlang_web/views/layout_view_test.exs b/test/something_erlang_web/views/layout_view_test.exs
deleted file mode 100644
index 8662744..0000000
--- a/test/something_erlang_web/views/layout_view_test.exs
+++ /dev/null
@@ -1,8 +0,0 @@
-defmodule SomethingErlangWeb.LayoutViewTest do
- use SomethingErlangWeb.ConnCase, async: true
-
- # When testing helpers, you may want to import Phoenix.HTML and
- # use functions such as safe_to_string() to convert the helper
- # result into an HTML string.
- # import Phoenix.HTML
-end
diff --git a/test/something_erlang_web/views/page_view_test.exs b/test/something_erlang_web/views/page_view_test.exs
deleted file mode 100644
index 246dc64..0000000
--- a/test/something_erlang_web/views/page_view_test.exs
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule SomethingErlangWeb.PageViewTest do
- use SomethingErlangWeb.ConnCase, async: true
-end
diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex
index fa8caae..5c19c5c 100644
--- a/test/support/conn_case.ex
+++ b/test/support/conn_case.ex
@@ -19,15 +19,15 @@ defmodule SomethingErlangWeb.ConnCase do
using do
quote do
+ # The default endpoint for testing
+ @endpoint SomethingErlangWeb.Endpoint
+
+ use SomethingErlangWeb, :verified_routes
+
# Import conveniences for testing with connections
import Plug.Conn
import Phoenix.ConnTest
import SomethingErlangWeb.ConnCase
-
- alias SomethingErlangWeb.Router.Helpers, as: Routes
-
- # The default endpoint for testing
- @endpoint SomethingErlangWeb.Endpoint
end
end
diff --git a/test/support/fixtures/forums_fixtures.ex b/test/support/fixtures/forums_fixtures.ex
deleted file mode 100644
index 9076af1..0000000
--- a/test/support/fixtures/forums_fixtures.ex
+++ /dev/null
@@ -1,21 +0,0 @@
-defmodule SomethingErlang.ForumsFixtures do
- @moduledoc """
- This module defines test helpers for creating
- entities via the `SomethingErlang.Forums` context.
- """
-
- @doc """
- Generate a thread.
- """
- def thread_fixture(attrs \\ %{}) do
- {:ok, thread} =
- attrs
- |> Enum.into(%{
- thread_id: 42,
- title: "some title"
- })
- |> SomethingErlang.Forums.create_thread()
-
- thread
- end
-end