spazio-solazzo/test/spazio_solazzo_web/live/user/profile_live_test.exs
Víctor Martínez 2cbce8ec39
refactor: make phone number optional (#9)
This pull request makes phone numbers optional for both user registrations and bookings, and updates validation, database schema, and UI to reflect this change. The main focus is to ensure that users are no longer required to provide a phone number, and that the application gracefully handles cases where a phone number is absent.

**Database & Resource Model Updates**

* Made the `phone_number` attribute in the `users` table and the `customer_phone` attribute in the `bookings` table nullable, including migration and resource snapshot updates. [[1]](diffhunk://#diff-baa6aed3674c4d6cbbebeafb076662df02dc4c25231dbd9dc9c8f0534ed1a1bfR1-R29) [[2]](diffhunk://#diff-a401f66b2ae5bfb798eb1bc2221bfeeac943e258950c90d59570b0bae05d3664R1-R244) [[3]](diffhunk://#diff-0c1180d6f6abc19b5987c8703bdee9ef67905535202f950e8327c32bd5b89d8aR1-R82)
* Updated Ash resource definitions in `user.ex` and `booking.ex` to allow `phone_number` and `customer_phone` to be `nil`. [[1]](diffhunk://#diff-9194b9d80dce091f6dcb56f784217272ae160e35454c4b4ccc8850ad5ee06e38L152-R152) [[2]](diffhunk://#diff-4b1ddd6d86899f2144c69d142883b8719c755e32c03dbda5da2188208a5ad503L55-R55) [[3]](diffhunk://#diff-4b1ddd6d86899f2144c69d142883b8719c755e32c03dbda5da2188208a5ad503L170-R170)

**Validation & Parsing Logic**

* Renamed and refactored user registration field validation to `ParseRegistrationFields`, allowing phone numbers to be omitted and trimming input values. Empty phone numbers are now treated as absent rather than as errors. [[1]](diffhunk://#diff-8ffdd76e260e3cda6f0816c8e585ae76b993a90d2519c38185a5fe22b4b49e47L1-R1) [[2]](diffhunk://#diff-8ffdd76e260e3cda6f0816c8e585ae76b993a90d2519c38185a5fe22b4b49e47R14-R60)
* Updated the authentication callback logic to trim input values and omit the phone number parameter if it is blank.

**User Interface Improvements**

* Updated registration and booking forms to indicate that phone numbers are optional, removed the required attribute, and improved placeholder text. [[1]](diffhunk://#diff-f356eb84970d8c9ee6ff1992c297b0cae07bade37ff967c1e6e0de6f8b67081cL101-R115) [[2]](diffhunk://#diff-43c0e1f7a869ee5c43a911bc10dc80cbb265a8672340ef0fa7c1d3009c047f02L92-R92)
* Updated email templates and confirmation screens to display "N/A" or "-" when phone numbers are missing. [[1]](diffhunk://#diff-48468ef2d1bb2c33b5ffb40457b77532815c7faf1830932661f665bff58b2177R6-R11) [[2]](diffhunk://#diff-3f33187b4021450b481ce53fe13166addea582c627f2cfbc99c75c7ce5c34857L10-R10) [[3]](diffhunk://#diff-43c0e1f7a869ee5c43a911bc10dc80cbb265a8672340ef0fa7c1d3009c047f02L92-R92)

**Profile Management**

* Improved profile update flow to ensure the form reflects the latest user data after saving changes.
* Made the "Full Name" field explicitly required in the profile form UI.
2026-01-15 15:57:54 +01:00

217 lines
5.4 KiB
Elixir

defmodule SpazioSolazzoWeb.ProfileLiveTest do
use SpazioSolazzoWeb.ConnCase, async: true
import Phoenix.LiveViewTest
alias SpazioSolazzo.Accounts.User
alias SpazioSolazzo.BookingSystem
alias SpazioSolazzo.BookingSystem.Booking
setup %{conn: conn} do
user = register_user("test@example.com", "Test User", "+123456789")
conn = log_in_user(conn, user)
%{user: user, conn: conn}
end
describe "ProfileLive - Profile Update" do
test "displays current user information", %{conn: conn, user: user} do
{:ok, _view, html} = live(conn, ~p"/profile")
assert html =~ user.name
assert html =~ user.phone_number
assert html =~ to_string(user.email)
end
test "successfully updates profile with valid data", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/profile")
result =
view
|> form("#profile-form", %{
form: %{
name: "Updated Test Name",
phone_number: "+9876543210"
}
})
|> render_submit()
assert result =~ "Profile updated successfully"
assert result =~ "+9876543210"
assert result =~ "Updated Test Name"
end
test "shows validation errors for invalid data", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/profile")
result =
view
|> form("#profile-form", %{
form: %{
name: "",
phone_number: "+9876543210"
}
})
|> render_change()
assert result =~ "is required" || result =~ "can't be blank"
end
test "email field is read-only", %{conn: conn, user: user} do
{:ok, _view, html} = live(conn, ~p"/profile")
assert html =~ "readonly"
assert html =~ to_string(user.email)
assert html =~ "Email cannot be changed"
end
end
describe "ProfileLive - Account Deletion" do
test "displays danger zone with delete account button", %{conn: conn} do
{:ok, view, html} = live(conn, ~p"/profile")
assert html =~ "Danger Zone"
assert has_element?(view, "button", "Delete My Account")
end
test "shows confirmation modal when delete button is clicked", %{conn: conn} do
{:ok, view, html} = live(conn, ~p"/profile")
refute html =~ "Confirm Account Deletion"
html =
view
|> element("button", "Delete My Account")
|> render_click()
assert html =~ "Confirm Account Deletion"
assert html =~ "Yes, Delete My Account"
end
test "can toggle delete history checkbox", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/profile")
view
|> element("button", "Delete My Account")
|> render_click()
html =
view
|> element("#gdpr-consent")
|> render_click()
assert html =~ "checked"
end
test "successfully deletes account and redirects to sign out", %{conn: conn, user: user} do
{:ok, view, _html} = live(conn, ~p"/profile")
view
|> element("button", "Delete My Account")
|> render_click()
view
|> element("button", "Yes, Delete My Account")
|> render_click()
assert_redirect(view, "/sign-out")
assert {:error, _} = Ash.get(User, user.id)
end
test "account deletion with delete_history=false preserves bookings", %{
conn: conn,
user: user
} do
{_space, asset, time_slot} = create_booking_fixtures()
{:ok, booking} =
BookingSystem.create_booking(
time_slot.id,
asset.id,
user.id,
Date.utc_today(),
"Test User",
"test@example.com",
"+1234567890",
"test booking"
)
booking_id = booking.id
{:ok, view, _html} = live(conn, ~p"/profile")
view
|> element("button", "Delete My Account")
|> render_click()
view
|> element("button", "Yes, Delete My Account")
|> render_click()
{:ok, preserved_booking} = Ash.get(Booking, booking_id)
assert preserved_booking.user_id == nil
assert preserved_booking.customer_name == "Test User"
end
test "account deletion with delete_history=true removes all bookings", %{
conn: conn,
user: user
} do
{_space, asset, time_slot} = create_booking_fixtures()
{:ok, booking} =
BookingSystem.create_booking(
time_slot.id,
asset.id,
user.id,
Date.utc_today(),
"Test User",
"test@example.com",
"+1234567890",
"test booking"
)
booking_id = booking.id
{:ok, view, _html} = live(conn, ~p"/profile")
view
|> element("button", "Delete My Account")
|> render_click()
view
|> element("#gdpr-consent")
|> render_click()
view
|> element("button", "Yes, Delete My Account")
|> render_click()
assert {:error, _} = Ash.get(Booking, booking_id)
end
end
defp create_booking_fixtures do
unique_id = :erlang.unique_integer([:positive, :monotonic])
{:ok, space} =
BookingSystem.create_space(
"Test Space #{unique_id}",
"test-space-#{unique_id}",
"Test description"
)
{:ok, asset} = BookingSystem.create_asset("Test Asset", space.id)
{:ok, time_slot} =
BookingSystem.create_time_slot_template(
~T[09:00:00],
~T[18:00:00],
:monday,
space.id
)
{space, asset, time_slot}
end
end