reset_password styling & live form working; other updates & fixes
This commit is contained in:
parent
4a87fb9baa
commit
24502e2667
17 changed files with 215 additions and 113 deletions
|
@ -11,12 +11,16 @@ import "../node_modules/bootstrap-icons/icons/person-circle.svg"; // accounts me
|
||||||
import "../node_modules/bootstrap-icons/icons/person-plus.svg"; // new user / register
|
import "../node_modules/bootstrap-icons/icons/person-plus.svg"; // new user / register
|
||||||
import "../node_modules/bootstrap-icons/icons/box-arrow-in-left.svg"; // log in
|
import "../node_modules/bootstrap-icons/icons/box-arrow-in-left.svg"; // log in
|
||||||
import "../node_modules/bootstrap-icons/icons/box-arrow-right.svg"; // log out
|
import "../node_modules/bootstrap-icons/icons/box-arrow-right.svg"; // log out
|
||||||
|
import "../node_modules/bootstrap-icons/icons/sliders.svg"; // new user / register
|
||||||
// forms etc
|
// forms etc
|
||||||
import "../node_modules/bootstrap-icons/icons/at.svg";
|
import "../node_modules/bootstrap-icons/icons/at.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/key.svg";
|
import "../node_modules/bootstrap-icons/icons/key.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/key-fill.svg";
|
import "../node_modules/bootstrap-icons/icons/key-fill.svg";
|
||||||
|
import "../node_modules/bootstrap-icons/icons/lock.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/shield-lock.svg";
|
import "../node_modules/bootstrap-icons/icons/shield-lock.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/arrow-repeat.svg";
|
import "../node_modules/bootstrap-icons/icons/arrow-repeat.svg";
|
||||||
|
import "../node_modules/bootstrap-icons/icons/door-open.svg"; // log in
|
||||||
|
import "../node_modules/@mdi/svg/svg/head-question-outline.svg"; // brand
|
||||||
|
|
||||||
// webpack automatically bundles all modules in your
|
// webpack automatically bundles all modules in your
|
||||||
// entry points. Those entry points can be configured
|
// entry points. Those entry points can be configured
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule Bones73kWeb.UserAuth do
|
defmodule Bones73kWeb.UserAuth do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
import Phoenix.Controller
|
import Phoenix.Controller
|
||||||
|
import Phoenix.HTML
|
||||||
|
|
||||||
alias Bones73k.Accounts
|
alias Bones73k.Accounts
|
||||||
alias Bones73kWeb.Router.Helpers, as: Routes
|
alias Bones73kWeb.Router.Helpers, as: Routes
|
||||||
|
@ -28,14 +29,17 @@ defmodule Bones73kWeb.UserAuth do
|
||||||
"""
|
"""
|
||||||
def log_in_user(conn, user, params \\ %{}) do
|
def log_in_user(conn, user, params \\ %{}) do
|
||||||
token = Accounts.generate_user_session_token(user)
|
token = Accounts.generate_user_session_token(user)
|
||||||
user_return_to = get_session(conn, :user_return_to)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> renew_session()
|
|> renew_session()
|
||||||
|> put_session(:user_token, token)
|
|> put_session(:user_token, token)
|
||||||
|> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}")
|
|> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}")
|
||||||
|> maybe_write_remember_me_cookie(token, params)
|
|> maybe_write_remember_me_cookie(token, params)
|
||||||
|> redirect(to: user_return_to || signed_in_path(conn))
|
|> put_flash(
|
||||||
|
:info,
|
||||||
|
raw("Welcome back, #{user.email} — you were logged in successfuly.")
|
||||||
|
)
|
||||||
|
|> redirect(to: get_session(conn, :user_return_to) || signed_in_path(conn))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_write_remember_me_cookie(conn, token, %{"remember_me" => "true"}) do
|
defp maybe_write_remember_me_cookie(conn, token, %{"remember_me" => "true"}) do
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
defmodule Bones73kWeb.UserResetPasswordController do
|
defmodule Bones73kWeb.UserResetPasswordController do
|
||||||
use Bones73kWeb, :controller
|
use Bones73kWeb, :controller
|
||||||
|
import Phoenix.LiveView.Controller
|
||||||
|
|
||||||
alias Bones73k.Accounts
|
alias Bones73k.Accounts
|
||||||
|
|
||||||
plug(:get_user_by_reset_password_token when action in [:edit, :update])
|
plug(:get_user_by_reset_password_token when action in [:edit])
|
||||||
|
|
||||||
def new(conn, _params) do
|
def new(conn, _params) do
|
||||||
render(conn, "new.html")
|
render(conn, "new.html")
|
||||||
|
@ -21,34 +22,20 @@ defmodule Bones73kWeb.UserResetPasswordController do
|
||||||
conn
|
conn
|
||||||
|> put_flash(
|
|> put_flash(
|
||||||
:info,
|
:info,
|
||||||
"If your email is in our system, you will receive instructions to reset your password shortly."
|
"If your email is in our system, you'll receive instructions to reset your password shortly."
|
||||||
)
|
)
|
||||||
|> redirect(to: "/")
|
|> redirect(to: "/")
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit(conn, _params) do
|
def edit(conn, _params) do
|
||||||
render(conn, "edit.html", changeset: Accounts.change_user_password(conn.assigns.user))
|
live_render(conn, Bones73kWeb.UserLive.ResetPassword)
|
||||||
end
|
|
||||||
|
|
||||||
# Do not log in the user after reset password to avoid a
|
|
||||||
# leaked token giving the user access to the account.
|
|
||||||
def update(conn, %{"user" => user_params}) do
|
|
||||||
case Accounts.reset_user_password(conn.assigns.user, user_params) do
|
|
||||||
{:ok, _} ->
|
|
||||||
conn
|
|
||||||
|> put_flash(:info, "Password reset successfully.")
|
|
||||||
|> redirect(to: Routes.user_session_path(conn, :new))
|
|
||||||
|
|
||||||
{:error, changeset} ->
|
|
||||||
render(conn, "edit.html", changeset: changeset)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_user_by_reset_password_token(conn, _opts) do
|
defp get_user_by_reset_password_token(conn, _opts) do
|
||||||
%{"token" => token} = conn.params
|
%{"token" => token} = conn.params
|
||||||
|
|
||||||
if user = Accounts.get_user_by_reset_password_token(token) do
|
if user = Accounts.get_user_by_reset_password_token(token) do
|
||||||
conn |> assign(:user, user) |> assign(:token, token)
|
put_session(conn, "user_id", user.id)
|
||||||
else
|
else
|
||||||
conn
|
conn
|
||||||
|> put_flash(:error, "Reset password link is invalid or it has expired.")
|
|> put_flash(:error, "Reset password link is invalid or it has expired.")
|
||||||
|
|
|
@ -6,7 +6,6 @@ defmodule Bones73kWeb.UserSessionController do
|
||||||
alias Bones73kWeb.UserAuth
|
alias Bones73kWeb.UserAuth
|
||||||
|
|
||||||
def new(conn, _params) do
|
def new(conn, _params) do
|
||||||
# IO.inspect(conn.private, label: "session_new conn.private :")
|
|
||||||
render(conn, "new.html", error_message: nil)
|
render(conn, "new.html", error_message: nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
class: input_class(f, :email, "form-control"),
|
class: input_class(f, :email, "form-control"),
|
||||||
placeholder: "e.g., babka@73k.us",
|
placeholder: "e.g., babka@73k.us",
|
||||||
maxlength: User.max_email,
|
maxlength: User.max_email,
|
||||||
required: true,
|
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
phx_debounce: "blur",
|
phx_debounce: "blur",
|
||||||
aria_describedby: error_id(f, :email)
|
aria_describedby: error_id(f, :email)
|
||||||
|
@ -38,7 +37,6 @@
|
||||||
<%= password_input f, :password,
|
<%= password_input f, :password,
|
||||||
value: input_value(f, :password),
|
value: input_value(f, :password),
|
||||||
class: input_class(f, :password, "form-control"),
|
class: input_class(f, :password, "form-control"),
|
||||||
minlength: User.min_password,
|
|
||||||
maxlength: User.max_password,
|
maxlength: User.max_password,
|
||||||
required: true,
|
required: true,
|
||||||
phx_debounce: "200",
|
phx_debounce: "200",
|
||||||
|
|
40
lib/bones73k_web/live/user/reset_password.ex
Normal file
40
lib/bones73k_web/live/user/reset_password.ex
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
defmodule Bones73kWeb.UserLive.ResetPassword do
|
||||||
|
use Bones73kWeb, :live_view
|
||||||
|
|
||||||
|
alias Bones73k.Accounts
|
||||||
|
alias Bones73k.Accounts.User
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def mount(_params, session, socket) do
|
||||||
|
user = Accounts.get_user!(session["user_id"])
|
||||||
|
|
||||||
|
socket
|
||||||
|
|> assign_defaults(session)
|
||||||
|
|> assign(page_title: "Reset password")
|
||||||
|
|> assign(changeset: Accounts.change_user_password(user))
|
||||||
|
|> assign(user: user)
|
||||||
|
|> live_okreply()
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_event("validate", %{"user" => user_params}, socket) do
|
||||||
|
cs = Accounts.change_user_password(socket.assigns.user, user_params)
|
||||||
|
{:noreply, socket |> assign(changeset: %{cs | action: :update})}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("save", %{"user" => user_params}, socket) do
|
||||||
|
case Accounts.reset_user_password(socket.assigns.user, user_params) do
|
||||||
|
{:ok, _} ->
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> put_flash(:success, "Password reset successfully.")
|
||||||
|
|> redirect(to: Routes.user_session_path(socket, :new))}
|
||||||
|
|
||||||
|
{:error, changeset} ->
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> put_flash(:error, "Please check the errors below.")
|
||||||
|
|> assign(changeset: %{changeset | action: :update})}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
57
lib/bones73k_web/live/user/reset_password.html.leex
Normal file
57
lib/bones73k_web/live/user/reset_password.html.leex
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-sm-9 col-md-7 col-lg-5 col-xl-4 ">
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
<%= icon_div @socket, "bi-shield-lock", [class: "icon baseline"] %>
|
||||||
|
Reset password
|
||||||
|
</h3>
|
||||||
|
<p class="lead">Hi <%= @user.email %> — What new word of passage will confirm you are who you say you are?</p>
|
||||||
|
|
||||||
|
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "pw_reset_form"], fn f -> %>
|
||||||
|
|
||||||
|
<div class="mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
|
||||||
|
<%= label f, :password, "New password", class: "form-label" %>
|
||||||
|
<div class="input-group has-validation">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<%= icon_div @socket, "bi-key", [class: "icon fs-5"] %>
|
||||||
|
</span>
|
||||||
|
<%= password_input f, :password,
|
||||||
|
value: input_value(f, :password),
|
||||||
|
class: input_class(f, :password, "form-control"),
|
||||||
|
maxlength: User.max_password,
|
||||||
|
autofocus: true,
|
||||||
|
aria_describedby: error_id(f, :password)
|
||||||
|
%>
|
||||||
|
<%= error_tag f, :password %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>">
|
||||||
|
<%= label f, :password_confirmation, "Confirm new password", class: "form-label" %>
|
||||||
|
<div class="input-group has-validation">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<%= icon_div @socket, "bi-key-fill", [class: "icon fs-5"] %>
|
||||||
|
</span>
|
||||||
|
<%= password_input f, :password_confirmation,
|
||||||
|
value: input_value(f, :password_confirmation),
|
||||||
|
class: input_class(f, :password_confirmation, "form-control"),
|
||||||
|
maxlength: User.max_password,
|
||||||
|
aria_describedby: error_id(f, :password_confirmation)
|
||||||
|
%>
|
||||||
|
<%= error_tag f, :password_confirmation %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<%= submit "Reset password", disabled: !@changeset.valid?, class: "btn btn-primary", phx_disable_with: "Saving..." %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<p class="mt-3 is-pulled-right">
|
||||||
|
<%= link "Register", to: Routes.user_registration_path(@socket, :new) %> |
|
||||||
|
<%= link "Log in", to: Routes.user_session_path(@socket, :new) %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -60,19 +60,12 @@ defmodule Bones73kWeb.Router do
|
||||||
scope "/", Bones73kWeb do
|
scope "/", Bones73kWeb do
|
||||||
pipe_through([:browser, :redirect_if_user_is_authenticated])
|
pipe_through([:browser, :redirect_if_user_is_authenticated])
|
||||||
|
|
||||||
# # liveview user auth routes
|
|
||||||
# live "/users/reset_password", UserLive.ResetPassword, :new
|
|
||||||
# live "/users/reset_password/:token", UserLive.ResetPassword, :edit
|
|
||||||
|
|
||||||
# original user auth routes from phx.gen.auth
|
|
||||||
get("/users/register", UserRegistrationController, :new)
|
get("/users/register", UserRegistrationController, :new)
|
||||||
get("/users/log_in", UserSessionController, :new)
|
get("/users/log_in", UserSessionController, :new)
|
||||||
post("/users/log_in", UserSessionController, :create)
|
post("/users/log_in", UserSessionController, :create)
|
||||||
# TODO:
|
|
||||||
get("/users/reset_password", UserResetPasswordController, :new)
|
get("/users/reset_password", UserResetPasswordController, :new)
|
||||||
post("/users/reset_password", UserResetPasswordController, :create)
|
post("/users/reset_password", UserResetPasswordController, :create)
|
||||||
get("/users/reset_password/:token", UserResetPasswordController, :edit)
|
get("/users/reset_password/:token", UserResetPasswordController, :edit)
|
||||||
put("/users/reset_password/:token", UserResetPasswordController, :update)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Bones73kWeb do
|
scope "/", Bones73kWeb do
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
<li>
|
<li>
|
||||||
<%= link nav_link_opts(@conn, to: Routes.user_settings_path(@conn, :edit), class: "dropdown-item") do %>
|
<%= link nav_link_opts(@conn, to: Routes.user_settings_path(@conn, :edit), class: "dropdown-item") do %>
|
||||||
|
<%= icon_div @conn, "bi-sliders", [class: "icon baseline me-1"] %>
|
||||||
Settings
|
Settings
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<%= icon_div @conn, "bi-arrow-repeat", [class: "icon baseline"] %>
|
<%= icon_div @conn, "bi-arrow-repeat", [class: "icon baseline"] %>
|
||||||
Resend confirmation instructions
|
Resend confirmation instructions
|
||||||
</h3>
|
</h3>
|
||||||
<p class="lead">We'll send you another email with a link to confirm your email address.</p>
|
<p class="lead">We'll send you another email with instructions to confirm your email address.</p>
|
||||||
|
|
||||||
<%= form_for :user, Routes.user_confirmation_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %>
|
<%= form_for :user, Routes.user_confirmation_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %>
|
||||||
|
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
<h1>Reset password</h1>
|
|
||||||
|
|
||||||
<%= form_for @changeset, Routes.user_reset_password_path(@conn, :update, @token), fn f -> %>
|
|
||||||
<%= if @changeset.action do %>
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
<p>Oops, something went wrong! Please check the errors below.</p>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<%= label f, :password, "New password" %>
|
|
||||||
<%= password_input f, :password, required: true %>
|
|
||||||
<%= error_tag f, :password %>
|
|
||||||
|
|
||||||
<%= label f, :password_confirmation, "Confirm new password" %>
|
|
||||||
<%= password_input f, :password_confirmation, required: true %>
|
|
||||||
<%= error_tag f, :password_confirmation %>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<%= submit "Reset password" %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
|
|
||||||
<%= link "Log in", to: Routes.user_session_path(@conn, :new) %>
|
|
||||||
</p>
|
|
|
@ -1,15 +1,38 @@
|
||||||
<h1>Forgot your password?</h1>
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-sm-9 col-md-7 col-lg-5 col-xl-4 ">
|
||||||
|
|
||||||
<%= form_for :user, Routes.user_reset_password_path(@conn, :create), fn f -> %>
|
<h3>
|
||||||
<%= label f, :email %>
|
<%= icon_div @conn, "mdi-head-question-outline", [class: "icon baseline"] %>
|
||||||
<%= email_input f, :email, required: true %>
|
Forgot your password?
|
||||||
|
</h3>
|
||||||
|
<p class="lead">We'll send you an email with instructions to reset your password.</p>
|
||||||
|
|
||||||
|
<%= form_for :user, Routes.user_reset_password_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %>
|
||||||
|
|
||||||
|
<%= label f, :email, class: "form-label" %>
|
||||||
|
<div class="input-group has-validation mb-3">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<%= icon_div @conn, "bi-at", [class: "icon fs-5"] %>
|
||||||
|
</span>
|
||||||
|
<%= email_input f, :email,
|
||||||
|
placeholder: "e.g., babka@73k.us",
|
||||||
|
class: "form-control",
|
||||||
|
maxlength: User.max_email,
|
||||||
|
required: true,
|
||||||
|
autofocus: true
|
||||||
|
%>
|
||||||
|
<span class="invalid-feedback">must be a valid email address</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<%= submit "Send instructions to reset password", class: "btn btn-primary" %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
|
||||||
|
<%= link "Log in", to: Routes.user_session_path(@conn, :new) %>
|
||||||
|
</p>
|
||||||
|
|
||||||
<div>
|
|
||||||
<%= submit "Send instructions to reset password" %>
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
</div>
|
||||||
|
|
||||||
<p>
|
|
||||||
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
|
|
||||||
<%= link "Log in", to: Routes.user_session_path(@conn, :new) %>
|
|
||||||
</p>
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="col-sm-9 col-md-7 col-lg-5 col-xl-4 ">
|
<div class="col-sm-9 col-md-7 col-lg-5 col-xl-4 ">
|
||||||
|
|
||||||
<h3>
|
<h3>
|
||||||
<%= icon_div @conn, "bi-box-arrow-in-left", [class: "icon baseline"] %>
|
<%= icon_div @conn, "bi-door-open", [class: "icon baseline"] %>
|
||||||
Log in
|
Log in
|
||||||
</h3>
|
</h3>
|
||||||
<p class="lead">Who goes there?</p>
|
<p class="lead">Who goes there?</p>
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
<%= label f, :password, class: "form-label" %>
|
<%= label f, :password, class: "form-label" %>
|
||||||
<div class="input-group has-validation mb-3">
|
<div class="input-group has-validation mb-3">
|
||||||
<span class="input-group-text">
|
<span class="input-group-text">
|
||||||
<%= icon_div @conn, "bi-shield-lock", [class: "icon fs-5"] %>
|
<%= icon_div @conn, "bi-lock", [class: "icon fs-5"] %>
|
||||||
</span>
|
</span>
|
||||||
<%= password_input f, :password,
|
<%= password_input f, :password,
|
||||||
class: "form-control",
|
class: "form-control",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
defmodule Bones73kWeb.UserResetPasswordView do
|
defmodule Bones73kWeb.UserResetPasswordView do
|
||||||
use Bones73kWeb, :view
|
use Bones73kWeb, :view
|
||||||
|
alias Bones73k.Accounts.User
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,7 +13,7 @@ defmodule Bones73kWeb.UserResetPasswordControllerTest do
|
||||||
test "renders the reset password page", %{conn: conn} do
|
test "renders the reset password page", %{conn: conn} do
|
||||||
conn = get(conn, Routes.user_reset_password_path(conn, :new))
|
conn = get(conn, Routes.user_reset_password_path(conn, :new))
|
||||||
response = html_response(conn, 200)
|
response = html_response(conn, 200)
|
||||||
assert response =~ "<h1>Forgot your password?</h1>"
|
assert response =~ "Forgot your password?\n </h3>"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,9 +52,10 @@ defmodule Bones73kWeb.UserResetPasswordControllerTest do
|
||||||
%{token: token}
|
%{token: token}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders reset password", %{conn: conn, token: token} do
|
test "renders reset password with user_id in session", %{conn: conn, token: token, user: user} do
|
||||||
conn = get(conn, Routes.user_reset_password_path(conn, :edit, token))
|
conn = get(conn, Routes.user_reset_password_path(conn, :edit, token))
|
||||||
assert html_response(conn, 200) =~ "<h1>Reset password</h1>"
|
assert get_session(conn, "user_id") == user.id
|
||||||
|
assert html_response(conn, 200) =~ "Reset password\n </h3>"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not render reset password with invalid token", %{conn: conn} do
|
test "does not render reset password with invalid token", %{conn: conn} do
|
||||||
|
@ -73,41 +74,5 @@ defmodule Bones73kWeb.UserResetPasswordControllerTest do
|
||||||
|
|
||||||
%{token: token}
|
%{token: token}
|
||||||
end
|
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 =~ "<h1>Reset password</h1>"
|
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
defmodule Bones73kWeb.UserLive.RegistrationTest do
|
defmodule Bones73kWeb.UserLive.RegistrationTest do
|
||||||
use Bones73kWeb.ConnCase
|
use Bones73kWeb.ConnCase
|
||||||
|
|
||||||
# import Plug.Conn
|
|
||||||
# import Phoenix.ConnTest
|
|
||||||
import Phoenix.LiveViewTest
|
import Phoenix.LiveViewTest
|
||||||
import Bones73k.AccountsFixtures
|
import Bones73k.AccountsFixtures
|
||||||
|
|
||||||
|
@ -34,10 +32,11 @@ defmodule Bones73kWeb.UserLive.RegistrationTest do
|
||||||
assert html =~ "Register\n </h3>"
|
assert html =~ "Register\n </h3>"
|
||||||
assert html =~ "must be a valid email address"
|
assert html =~ "must be a valid email address"
|
||||||
assert html =~ "should be at least #{User.min_password()} character(s)"
|
assert html =~ "should be at least #{User.min_password()} character(s)"
|
||||||
|
assert html =~ "type=\"submit\" disabled=\"disabled\""
|
||||||
end
|
end
|
||||||
|
|
||||||
@tag :capture_log
|
@tag :capture_log
|
||||||
test "creates account and sets login params_token and phx-trigger-action", %{
|
test "creates account, sets login token & phx-trigger-action", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
user_return_to: user_return_to
|
user_return_to: user_return_to
|
||||||
} do
|
} do
|
57
test/bones73k_web/live/user/reset_password_test.exs
Normal file
57
test/bones73k_web/live/user/reset_password_test.exs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
defmodule Bones73kWeb.UserLive.ResetPasswordTest do
|
||||||
|
use Bones73kWeb.ConnCase
|
||||||
|
|
||||||
|
import Phoenix.LiveViewTest
|
||||||
|
import Bones73k.AccountsFixtures
|
||||||
|
|
||||||
|
alias Bones73k.Repo
|
||||||
|
alias Bones73k.Accounts
|
||||||
|
alias Bones73k.Accounts.User
|
||||||
|
alias Bones73k.Accounts.UserToken
|
||||||
|
|
||||||
|
setup %{conn: conn} do
|
||||||
|
user = user_fixture()
|
||||||
|
conn = init_test_session(conn, %{"user_id" => user.id})
|
||||||
|
%{conn: conn, user: user}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "displays registration form", %{conn: conn, user: user} do
|
||||||
|
{:ok, _view, html} = live_isolated(conn, Bones73kWeb.UserLive.ResetPassword)
|
||||||
|
|
||||||
|
assert html =~ "Reset password\n </h3>"
|
||||||
|
assert html =~ user.email
|
||||||
|
assert html =~ "New password</label>"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "render errors for invalid data", %{conn: conn} do
|
||||||
|
{:ok, view, _html} = live_isolated(conn, Bones73kWeb.UserLive.ResetPassword)
|
||||||
|
|
||||||
|
form_data = %{"user" => %{"password" => "abc", "password_confirmation" => "def"}}
|
||||||
|
html = form(view, "#pw_reset_form", form_data) |> render_change()
|
||||||
|
|
||||||
|
assert html =~ "Reset password\n </h3>"
|
||||||
|
assert html =~ "should be at least #{User.min_password()} character(s)"
|
||||||
|
assert html =~ "does not match password"
|
||||||
|
assert html =~ "type=\"submit\" disabled=\"disabled\""
|
||||||
|
end
|
||||||
|
|
||||||
|
@tag :capture_log
|
||||||
|
test "saves new password once", %{conn: conn, user: user} do
|
||||||
|
{:ok, view, _html} = live_isolated(conn, Bones73kWeb.UserLive.ResetPassword)
|
||||||
|
|
||||||
|
# Render submitting a new password
|
||||||
|
new_pw = "valid_new_pass_123"
|
||||||
|
form_data = %{"user" => %{"password" => new_pw, "password_confirmation" => new_pw}}
|
||||||
|
_html = form(view, "#pw_reset_form", form_data) |> render_submit()
|
||||||
|
|
||||||
|
# Confirm redirected
|
||||||
|
flash = assert_redirected(view, Routes.user_session_path(conn, :new))
|
||||||
|
assert flash["success"] == "Password reset successfully."
|
||||||
|
|
||||||
|
# Confirm password was updated
|
||||||
|
assert Accounts.get_user_by_email_and_password(user.email, new_pw)
|
||||||
|
|
||||||
|
# Tokens have been deleted
|
||||||
|
assert [] == Repo.all(UserToken.user_and_contexts_query(user, :all))
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue