Skip to content
Snippets Groups Projects
Commit 7fd3a4cc authored by maripuus's avatar maripuus
Browse files

Merge branch '16-fr-06-update-profile' into 'main'

Resolve "FR-06: Update Profile"

Closes #16

See merge request !47
parents 50f5664e 8299ecfa
No related branches found
No related tags found
1 merge request!47Resolve "FR-06: Update Profile"
Pipeline #47198 passed
Showing
with 436 additions and 41 deletions
defmodule WhiteBreadConfig do
use WhiteBread.SuiteConfiguration
suite name: "User Registration Features",
context: UserRegistrationContext,
feature_paths: ["features/user_registration.feature"]
......@@ -81,4 +82,9 @@ defmodule WhiteBreadConfig do
context: NavigationBarContext,
feature_paths: ["features/navigation_bar.feature"]
suite name: "FR6 - 1.6 Profile Update Features",
context: ProfileUpdateContext,
feature_paths: ["features/profile_update.feature"]
end
defmodule ProfileUpdateContext do
use WhiteBread.Context
use Hound.Helpers
alias PropTrackr.Accounts
alias PropTrackr.Repo
alias PropTrackr.Accounts.User
scenario_starting_state fn _state ->
Ecto.Adapters.SQL.Sandbox.checkout(PropTrackr.Repo)
Ecto.Adapters.SQL.Sandbox.mode(PropTrackr.Repo, {:shared, self()})
Hound.start_session()
%{registered_users: []}
end
scenario_finalize fn _status, state ->
# Enum.each(state[:registered_users], &Repo.delete/1)
Ecto.Adapters.SQL.Sandbox.checkin(PropTrackr.Repo)
Hound.end_session()
end
given_ ~r/^there exist following accounts$/, fn state,%{table_data: table} ->
table
|> Enum.map(fn user_details -> User.changeset(%User{}, user_details) end)
|> Enum.each(fn changeset -> Repo.insert!(changeset) end)
existing_user = List.first(table)
{
:ok,
state
|> Map.put(:email, existing_user[:email])
|> Map.put(:password, existing_user[:password])
}
end
and_ ~r/^I am logged in$/, fn state ->
navigate_to("/login")
fill_field({:id, "email"}, state[:email])
fill_field({:id, "password"}, state[:password])
click({:id, "login_button"})
assert visible_in_page? ~r"Successfully logged in!"
assert current_path() == "/"
{:ok, state}
end
and_ ~r/^I want to update my profile$/, fn state ->
navigate_to("/me")
click({:id, "edit_details"})
assert visible_in_page? ~r"Update your user"
assert current_path() == "/me/details"
{:ok, state}
end
and_ ~r/^I fill the profile update form with valid details$/, fn state, %{ table_data: table1 } ->
table = List.first(table1)
fill_field({:id, "name"}, table[:name])
fill_field({:id, "surname"}, table[:surname])
formatted_date = formatDate(table[:birth_date])
fill_field({:id, "birth_date"}, formatted_date)
fill_field({:id, "phone_number"}, table[:phone_number])
fill_field({:id, "bio"}, table[:bio])
{:ok, state}
end
when_ ~r/^I click update profile$/, fn state ->
click({:id, "update_profile"})
{:ok, state}
end
then_ ~r/^I should be redirected to profile page$/, fn state ->
assert current_path() == "/me"
{:ok, state}
end
and_ ~r/^I should see a success notification$/, fn state ->
assert visible_in_page? ~r"Profile details updated successfully!"
{:ok, state}
end
and_ ~r/^my profile should be updated with new info$/, fn state, %{ table_data: table1 } ->
table = List.first(table1)
formatted_date = formatDate(table[:birth_date])
assert visible_page_text() =~ (table[:name])
assert visible_page_text() =~(table[:surname])
assert visible_page_text() =~(table[:birth_date])
assert visible_page_text() =~(table[:phone_number])
assert visible_page_text() =~(table[:bio])
assert visible_page_text() =~(table[:email])
{:ok, state}
end
and_ ~r/^I fill the profile update form with invalid data$/, fn state, %{ table_data: table1 } ->
table = List.first(table1)
fill_field({:id, "name"}, table[:name])
fill_field({:id, "surname"}, table[:surname])
formatted_date = formatDate(table[:birth_date])
fill_field({:id, "birth_date"}, formatted_date)
fill_field({:id, "phone_number"}, table[:phone_number])
fill_field({:id, "bio"}, table[:bio])
{:ok, state}
end
then_ ~r/^I should see an error message stating invalid phone number$/, fn state ->
assert visible_page_text() =~ "must be between 3 and 15 digits, without '+' sign"
{:ok, state}
end
then_ ~r/^I should see an error message stating invalid birth date$/, fn state ->
assert visible_page_text() =~ "Birth date cannot be in the future"
{:ok, state}
end
and_ ~r/^I should still see the profile update form$/, fn state ->
assert visible_in_page? ~r"Update your user"
{:ok, state}
end
given_ ~r/^I am not logged in$/, fn state ->
navigate_to("/logout")
{:ok, state}
end
when_ ~r/^I try to access the profile update page$/, fn state ->
navigate_to("/me/details")
{:ok, state}
end
then_ ~r/^I should be redirected to the login page$/, fn state ->
assert current_path() == "/login"
{:ok, state}
end
and_ ~r/^I should see an error message stating that I am not logged in$/, fn state ->
assert visible_in_page? ~r"You are not logged in!"
{:ok, state}
end
defp formatDate(date) do
[year, month, day] = String.split(date, "-")
formatted_date = "#{month}-#{day}-#{year}"
formatted_date
end
end
Feature: 1.6 Profile Update
Scenario: Authenticated user can update their profile
Given there exist following accounts
| name | surname | birth_date | phone_number | bio | email | password | confirm_password |
| Existing | Account | 2000-01-01 | 000 | yoyo | existing.account@gmail.com | password | password |
And I am logged in
And I want to update my profile
And I fill the profile update form with valid details
| name | surname | birth_date | phone_number | bio |
| Updated Name | Updated Surname | 1990-01-01 | 987654321 | Updated Bio |
When I click update profile
Then I should be redirected to profile page
And I should see a success notification
And my profile should be updated with new info
| name | surname | birth_date | phone_number | bio | email |
| Updated Name | Updated Surname | 1990-01-01 | 987654321 | Updated Bio | existing.account@gmail.com |
Scenario: Authenticated user cannot update their profile with invalid phone number
Given there exist following accounts
| name | surname | birth_date | phone_number | bio | email | password | confirm_password |
| Existing | Account | 2000-01-01 | 987654321 | yoyo | existing.account@gmail.com | password | password |
And I am logged in
And I want to update my profile
And I fill the profile update form with invalid data
| name | surname | birth_date | phone_number | bio |
| Updated Name | Updated Surname | 1990-01-01 | 5 | Updated Bio |
When I click update profile
Then I should see an error message stating invalid phone number
And I should still see the profile update form
Scenario: Authenticated user cannot update their profile with a future birth date
Given there exist following accounts
| name | surname | birth_date | phone_number | bio | email | password | confirm_password |
| Existing | Account | 2000-01-01 | 987654321 | yoyo | existing.account@gmail.com | password | password |
And I am logged in
And I want to update my profile
And I fill the profile update form with invalid data
| name | surname | birth_date | phone_number | bio |
| Updated Name | Updated Surname | 2500-01-01 | 987654321 | Updated Bio |
When I click update profile
Then I should see an error message stating invalid birth date
And I should still see the profile update form
Scenario: Authenticated user cannot update their profile with missing required fields
Given there exist following accounts
| name | surname | birth_date | phone_number | bio | email | password | confirm_password |
| Existing | Account | 2000-01-01 | 987654321 | yoyo | existing.account@gmail.com | password | password |
And I am logged in
And I want to update my profile
And I fill the profile update form with invalid data
| name | surname | birth_date | phone_number | bio |
| | | 2000-01-01 | 987654321 | Updated Bio |
When I click update profile
And I should still see the profile update form
Scenario: Unauthenticated user cannot access the profile update page
Given I am not logged in
When I try to access the profile update page
Then I should be redirected to the login page
And I should see an error message stating that I am not logged in
......@@ -49,7 +49,7 @@ defmodule PropTrackr.Accounts.User do
|> validate_format(:phone_number, ~r/^\d{3,15}$/, message: "must be between 3 and 15 digits, without '+' sign")
|> validate_length(:name, min: 2, max: 50)
|> validate_length(:surname, min: 2, max: 50)
|> validate_date(:birth_date)
|> validate_date_update(:birth_date)
end
defp validate_date(changeset, date) do
......@@ -67,8 +67,38 @@ defmodule PropTrackr.Accounts.User do
changeset
end
end
|> validate_unique_email()
end
defp validate_date_update(changeset, date) do
case get_field(changeset, date) do
nil -> changeset
birth_date ->
cond do
Date.diff(Date.utc_today(), birth_date) < 0 ->
add_error(changeset, date, "Birth date cannot be in the future")
Date.diff(birth_date, ~D[1900-01-01]) < 0 ->
add_error(changeset, date, "Date is too past")
true ->
changeset
end
end
end
def register_changeset(struct, params \\ %{}) do
changeset(struct, params)
end
def login_changeset(struct, params \\ %{}) do
struct
|> cast(params, [:email, :password])
|> validate_required([:email, :password])
end
defp validate_password_confirmation(changeset, field_1, field_2) do
case get_field(changeset, field_1) do
nil -> changeset
......
......@@ -2,7 +2,7 @@
<%= @user.name %> <%= @user.surname %>'s profile
<:actions>
<.link href={~p"/me/details"}>
<.button>Edit details</.button>
<.button id="edit_details">Edit details</.button>
</.link>
<.link href={~p"/me/delete"}>
<.button class="bg-red-700 hover:bg-red-500">Delete account</.button>
......
defmodule PropTrackrWeb.UpdateController do
defmodule PropTrackrWeb.ProfileUpdateController do
use PropTrackrWeb, :controller
alias PropTrackr.Repo
......@@ -18,19 +18,20 @@ defmodule PropTrackrWeb.UpdateController do
end
end
def create(conn, %{"user" => user_params}) do
# changeset = User.changeset(%User{}, user_params)
def create(conn, %{"user" => user_params}) do # def create(conn, %{"user" => user_params}) do
user_id = get_session(conn, :user_id)
user = Repo.get(User, user_id)
changeset = User.update_changeset(user, user_params)
case Repo.update(changeset) do
{:ok, _user} ->
conn
|> put_flash(:info, "User updated successfully.")
|> redirect(to: ~p"/")
|> put_flash(:info, "Profile details updated successfully!")
|> redirect(to: ~p"/me")
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "index.html", user: user, changeset: changeset)
render(conn, "index.html", changeset: changeset)
end
end
end
defmodule PropTrackrWeb.ProfileUpdateHTML do
use PropTrackrWeb, :html
embed_templates "profile_update_html/*"
end
......@@ -2,12 +2,12 @@
Update your user
</.header>
<.simple_form :let={f} for={@changeset} action={~p"/me/details"} method="POST">
<.input field={f[:name]} type="text" required label="Name"/>
<.input field={f[:surname]} type="text" required label="Surname" />
<.input field={f[:birth_date]} type="text" required label="Birth-date" />
<.input field={f[:phone_number]} type="text" required label="Phone" />
<.input field={f[:bio]} type="text" required label="Bio" />
<.simple_form :let={f} for={@changeset} action={~p"/me/details"} method="POST" >
<.input field={f[:name]} id="name" type="text" required label="Name"/>
<.input field={f[:surname]} id="surname" type="text" required label="Surname" />
<.input field={f[:birth_date]} id="birth_date" type="date" required label="Birth-date" />
<.input field={f[:phone_number]} id="phone_number" type="text" required label="Phone" />
<.input field={f[:bio]} id="bio" type="text" required label="Bio" />
<:actions>
<.button id="update_profile">Update Profile</.button>
......
defmodule PropTrackrWeb.UpdateHTML do
use PropTrackrWeb, :html
embed_templates "update_html/*"
end
......@@ -29,7 +29,7 @@ defmodule PropTrackrWeb.Router do
# User profile
resources "/me", ProfileController, only: [:index]
resources "/me/details", UpdateController, only: [:index, :create]
resources "/me/details", ProfileUpdateController, only: [:index, :create]
resources "/me/password", PasswordController, only: [:index, :create]
resources "/me/properties", MyPropertiesController, only: [:index]
resources "/me/delete", UserDeleteController, only: [:index, :delete]
......
defmodule PropTrackrWeb.ProfileUpdateControllerTest do
use PropTrackrWeb.ConnCase
alias PropTrackr.Accounts.User
alias PropTrackr.Repo
@valid_attrs %{
name: "John",
surname: "Doe",
birth_date: ~D[1990-01-01],
phone_number: "123456789",
email: "john.doe@example.com",
password: "1234",
confirm_password: "1234",
hashed_password: Bcrypt.hash_pwd_salt("1234"),
bio: "Software Developer"
}
@normal_attrs %{
name: "Test",
surname: "Test",
birth_date: ~D[2001-06-06],
phone_number: "5555555",
bio: "Testestest34273094817203948"
}
@invalid_attrs %{
name: "1234",
surname: "",
birth_date: ~D[2500-01-01],
phone_number: "invalid",
bio: "Test"
}
setup do
user = %User{
name: "John",
surname: "Doe",
birth_date: ~D[1990-01-01],
phone_number: "123456789",
email: "john.doe@example.com",
hashed_password: Bcrypt.hash_pwd_salt("1234"),
bio: "Software Developer"
}
user = Repo.insert!(user)
{:ok, %{user: user}}
end
test "1.6 Authenticated user should be able to update their details", %{conn: conn, user: user} do
conn = setup_session(conn, user)
conn = conn
|> post("/me/details", %{
user: %{
name: @normal_attrs[:name],
surname: @normal_attrs[:surname],
birth_date: @normal_attrs[:birth_date],
phone_number: @normal_attrs[:phone_number],
bio: @normal_attrs[:bio],
}
})
assert redirected_to(conn) == "/me"
conn = get(conn, redirected_to(conn))
assert html_response(conn, 200)
assert get_flash(conn, :info) =~ ~r/Profile details updated successfully/
updated_user = Repo.get(User, user.id)
assert updated_user.name == @normal_attrs[:name]
assert updated_user.surname == @normal_attrs[:surname]
assert updated_user.birth_date == @normal_attrs[:birth_date]
assert updated_user.phone_number == @normal_attrs[:phone_number]
assert updated_user.bio == @normal_attrs[:bio]
end
test "1.6 Authenticated user should not be able to update data for invalid name", %{conn: conn, user: user} do
conn = setup_session(conn, user)
conn = conn
|> post("/me/details", %{
user: %{
name: @invalid_attrs[:name],
}
})
assert html_response(conn, 200) =~ "Update your user"
unchanged_user = Repo.get(User, user.id)
refute unchanged_user.name == @invalid_attrs[:name]
end
test "1.6 Authenticated user should not be able to update data for invalid surname", %{conn: conn, user: user} do
conn = setup_session(conn, user)
conn = conn
|> post("/me/details", %{
user: %{
surname: @invalid_attrs[:surname],
}
})
assert html_response(conn, 200) =~ "Update your user"
unchanged_user = Repo.get(User, user.id)
refute unchanged_user.surname == @invalid_attrs[:surname]
end
test "1.6 Authenticated user should not be able to update data for a future birth date", %{conn: conn, user: user} do
conn = setup_session(conn, user)
conn = conn
|> post("/me/details", %{
user: %{
birth_date: @invalid_attrs[:birth_date],
}
})
assert html_response(conn, 200) =~ "Update your user"
unchanged_user = Repo.get(User, user.id)
refute unchanged_user.birth_date == @invalid_attrs[:birth_date]
end
test "1.6 Authenticated user should not be able to update data for invalid phone number", %{conn: conn, user: user} do
conn = setup_session(conn, user)
conn = conn
|> post("/me/details", %{
user: %{
phone_number: @invalid_attrs[:phone_number],
}
})
assert html_response(conn, 200) =~ "Update your user"
unchanged_user = Repo.get(User, user.id)
refute unchanged_user.phone_number == @invalid_attrs[:phone_number]
end
defp setup_session(conn, user) do
conn = conn |> post("/login", email: user.email, password: "1234")
conn = get(conn, redirected_to(conn))
conn
end
end
......@@ -126,4 +126,5 @@ defmodule PropTrackrWeb.RegisterControllerTest do
assert Repo.get_by(User, email: @mismatched_password_attrs.email) == nil
end
end
end
defmodule PropTrackrWeb.UpdateControllerTest do
use PropTrackrWeb.ConnCase
alias PropTrackr.Accounts.User
alias PropTrackr.Repo
@user_credentials %{email: "test.user@gmail.com", password: "testing", new_password: "new_password"}
@password "1234"
setup do
user = %User{
name: "Test",
surname: "User",
birth_date: ~D[2000-01-01],
phone_number: "000",
bio: "Yo",
email: "test.user@gmail.com",
password: @password,
confirm_password: @password,
hashed_password: Bcrypt.hash_pwd_salt(@password)
}
Repo.insert!(user)
:ok
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment