All checks were successful
deploy / deploy (push) Successful in 3m28s
- Create dedicated /admin/account page for user account management - Move email, password, and 2FA settings from /admin/settings - Add Account link to top of admin sidebar navigation - Add TOTP-based two-factor authentication with NimbleTOTP - Add TOTP verification LiveView for login flow - Add AccountController for TOTP session management - Remove Advanced section from settings (duplicated in dev tools) - Remove user email from sidebar footer (replaced by Account link) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
124 lines
3.4 KiB
Elixir
124 lines
3.4 KiB
Elixir
defmodule BerrypodWeb.Admin.AccountTest do
|
|
use BerrypodWeb.ConnCase, async: false
|
|
|
|
import Phoenix.LiveViewTest
|
|
import Berrypod.AccountsFixtures
|
|
|
|
alias Berrypod.Accounts
|
|
|
|
setup do
|
|
user = user_fixture()
|
|
%{user: user}
|
|
end
|
|
|
|
describe "unauthenticated" do
|
|
test "redirects to login", %{conn: conn} do
|
|
{:error, redirect} = live(conn, ~p"/admin/account")
|
|
assert {:redirect, %{to: path}} = redirect
|
|
assert path == ~p"/users/log-in"
|
|
end
|
|
end
|
|
|
|
describe "account page" do
|
|
setup %{conn: conn, user: user} do
|
|
conn = log_in_user(conn, user)
|
|
%{conn: conn, user: user}
|
|
end
|
|
|
|
test "renders email and password forms", %{conn: conn, user: user} do
|
|
{:ok, view, html} = live(conn, ~p"/admin/account")
|
|
|
|
assert html =~ "Account"
|
|
assert html =~ user.email
|
|
assert has_element?(view, "#email_form")
|
|
assert has_element?(view, "#password_form")
|
|
end
|
|
|
|
test "validates email change", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, ~p"/admin/account")
|
|
|
|
result =
|
|
view
|
|
|> element("#email_form")
|
|
|> render_change(%{"user" => %{"email" => "with spaces"}})
|
|
|
|
assert result =~ "must have the @ sign and no spaces"
|
|
end
|
|
|
|
test "submits email change", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, ~p"/admin/account")
|
|
|
|
result =
|
|
view
|
|
|> form("#email_form", %{"user" => %{"email" => unique_user_email()}})
|
|
|> render_submit()
|
|
|
|
assert result =~ "A link to confirm your email"
|
|
end
|
|
|
|
test "validates password", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, ~p"/admin/account")
|
|
|
|
result =
|
|
view
|
|
|> element("#password_form")
|
|
|> render_change(%{
|
|
"user" => %{
|
|
"password" => "short",
|
|
"password_confirmation" => "mismatch"
|
|
}
|
|
})
|
|
|
|
assert result =~ "should be at least 12 character(s)"
|
|
end
|
|
|
|
test "submits valid password change", %{conn: conn, user: user} do
|
|
new_password = valid_user_password()
|
|
{:ok, view, _html} = live(conn, ~p"/admin/account")
|
|
|
|
form =
|
|
form(view, "#password_form", %{
|
|
"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"/admin/account"
|
|
assert Accounts.get_user_by_email_and_password(user.email, new_password)
|
|
end
|
|
end
|
|
|
|
describe "two-factor authentication" do
|
|
setup %{conn: conn, user: user} do
|
|
conn = log_in_user(conn, user)
|
|
%{conn: conn, user: user}
|
|
end
|
|
|
|
test "shows 2FA section", %{conn: conn} do
|
|
{:ok, _view, html} = live(conn, ~p"/admin/account")
|
|
|
|
assert html =~ "Two-factor authentication"
|
|
assert html =~ "Off"
|
|
assert html =~ "Enable 2FA"
|
|
end
|
|
|
|
test "shows enabled state when TOTP is enabled", %{conn: conn, user: user} do
|
|
# Enable TOTP for the user
|
|
secret = NimbleTOTP.secret()
|
|
code = NimbleTOTP.verification_code(secret)
|
|
{:ok, _user, _codes} = Accounts.enable_totp(user, secret, code)
|
|
|
|
{:ok, _view, html} = live(conn, ~p"/admin/account")
|
|
|
|
assert html =~ "Two-factor authentication"
|
|
assert html =~ "Enabled"
|
|
assert html =~ "Disable 2FA"
|
|
end
|
|
end
|
|
end
|