diff --git a/features/contexts/property_update_context.exs b/features/contexts/property_update_context.exs index a16db62b2415948a6a9cd622b696f67d1c0ba355..5e916f85b363bf214728b7dd015b78087586f0c4 100644 --- a/features/contexts/property_update_context.exs +++ b/features/contexts/property_update_context.exs @@ -21,104 +21,139 @@ defmodule PropertyUpdateContext do 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) + random_user = List.last(table) + + { + :ok, + state + |> Map.put(:email, existing_user[:email]) + |> Map.put(:password, existing_user[:password]) + |> Map.put(:random_email, random_user[:email]) + |> Map.put(:random_password, random_user[:password]) + } + end + + and_ ~r/^the following properties exist$/, fn state, %{ table_data: table } -> + logged_in_user = Repo.get_by(User, email: state[:email]) + + advertisements = + table + |> Enum.map(fn details -> details |> Map.put(:reference, Ecto.UUID.generate()) end) + |> Enum.map(fn details -> {Ecto.build_assoc(logged_in_user, :properties, details), details} end) + |> Enum.map(fn {assoc, details} -> Property.changeset(assoc, details) end) + |> Enum.map(fn changeset -> Repo.insert!(changeset) end) + + { + :ok, + state + |> Map.put(:advertisements, advertisements) + } + end + + and_ ~r/^I am logged in$/, fn state -> + setup_session(state[:email], state[:password]) {:ok, state} end - and_ ~r/^the following properties exist$/, fn state, %{table_data: table} -> - properties = table - |> Enum.map(fn property -> - user = Repo.get_by!(User, email: property[:user_email]) - property_params = Map.drop(property, [:user_email]) - |> Map.new(fn {key, value} -> - case key do - :type -> {:type, String.to_existing_atom(String.trim(value, ":"))} - :property_type -> {:property_type, String.to_existing_atom(String.trim(value, ":"))} - :state -> {:state, String.to_existing_atom(String.trim(value, ":"))} - _ -> {key, value} - end - end) + and_ ~r/^I navigate to my property's details page$/, fn state -> + advertisement = List.first(state[:advertisements]) + click({:id, "edit-#{advertisement.reference}"}) + {:ok, state} + end - property_assoc = Ecto.build_assoc(user, :properties, property_params) - Property.changeset(property_assoc, property_params) - |> Ecto.Changeset.put_change(:reference, Ecto.UUID.generate()) - |> Repo.insert!() - end) + when_ ~r/^I click the edit button$/, fn state -> + click({:id, "edit-property"}) + {:ok, state} + end - {:ok, Map.put(state, :property, List.first(properties))} + and_ ~r/^I update the following fields$/, fn state, %{ table_data: table } -> + data = List.first(table) + fill_field({:id, "title"}, data[:title]) + fill_field({:id, "description"}, data[:description]) + select_dropdown("type", data[:type]) + select_dropdown("property_type", data[:property_type]) + select_dropdown("state", data[:state]) + fill_field({:id, "location"}, data[:location]) + fill_field({:id, "room_count"}, data[:room_count]) + fill_field({:id, "area"}, data[:area]) + fill_field({:id, "floor"}, data[:floor]) + fill_field({:id, "price"}, data[:price]) + { + :ok, + state + |> Map.put(:updated_data, data) + } end - and_ ~r/^I am logged in$/, fn state -> - navigate_to("/login") - fill_field({:id, "email"}, "existing.account@gmail.com") - fill_field({:id, "password"}, "password") - click({:id, "login_button"}) + and_ ~r/^I click save changes$/, fn state -> + click({:id, "save-changes"}) {:ok, state} + end - assert visible_in_page?(~r/Successfully logged in!/) + then_ ~r/^I should see a success message$/, fn state -> + assert visible_in_page? ~r/Property updated successfully./ + {:ok, state} end - and_ ~r/^I navigate to my property's details page$/, fn state -> - navigate_to("/properties/#{state.property.reference}") + and_ ~r/^I should be redirected back to the property page$/, fn state -> + advertisement = List.first(state[:advertisements]) + assert current_path() == "/properties/#{advertisement.reference}" {:ok, state} end - when_ ~r/^I click the edit button$/, fn state -> - edit_button = find_element(:class, "edit-property-button") - click(edit_button) + and_ ~r/^I should see the updated property details$/, fn state -> + advertisement = state[:updated_data] + assert visible_in_page? ~r/#{advertisement.title}/ + assert visible_in_page? ~r/#{advertisement.description}/ + assert visible_in_page? ~r/#{advertisement.type}/i + assert visible_in_page? ~r/#{advertisement.property_type}/i + assert visible_in_page? ~r/#{advertisement.location}/ + assert visible_in_page? ~r/#{advertisement.price}/ + assert visible_in_page? ~r/#{advertisement.room_count}/ + assert visible_in_page? ~r/#{advertisement.area}/ + assert visible_in_page? ~r/#{advertisement.floor}/ + assert visible_in_page? ~r/#{advertisement.state}/i + {:ok, state} end - and_ ~r/^I update the following fields$/, fn state, %{table_data: [fields]} -> - fields = Map.new(fields, fn {key, value} -> - case key do - :type -> {:type, String.to_existing_atom(String.trim(value, ":"))} - :property_type -> {:property_type, String.to_existing_atom(String.trim(value, ":"))} - :state -> {:state, String.to_existing_atom(String.trim(value, ":"))} - _ -> {key, value} - end - end) - - fill_field({:id, "property_title"}, fields[:title]) - fill_field({:id, "property_description"}, fields[:description]) - fill_field({:id, "property_location"}, fields[:location]) - fill_field({:id, "property_room_count"}, fields[:room_count]) - fill_field({:id, "property_area"}, fields[:area]) - fill_field({:id, "property_floor"}, fields[:floor]) - fill_field({:id, "property_floor_count"}, fields[:floor_count]) - fill_field({:id, "property_price"}, fields[:price]) - - find_element(:id, "property_type") - |> click() - find_element(:css, "option[value='#{fields[:type]}']") - |> click() - - find_element(:id, "property_property_type") - |> click() - find_element(:css, "option[value='#{fields[:property_type]}']") - |> click() - - find_element(:id, "property_state") - |> click() - find_element(:css, "option[value='#{fields[:state]}']") - |> click() + then_ ~r/^I should see error message$/, fn state -> + assert visible_in_page? ~r/Oops, something went wrong! Please check the errors below./ + {:ok, state} + end + and_ ~r/^I should see the edit page again$/, fn state -> + assert visible_in_page? ~r/Edit Property/ {:ok, state} end - and_ ~r/^I click save changes$/, fn state -> - click({:id, "save-changes-button"}) + and_ ~r/^I am logged in as a random user who does not own the advertisement$/, fn state -> + setup_session(state[:random_email], state[:random_password]) {:ok, state} end - then_ ~r/^I should see a success message$/, fn state -> - assert visible_in_page?(~r/Property updated successfully/) + and_ ~r/^I navigate to the property's details page$/, fn state -> + advertisement = List.first(state[:advertisements]) + click({:id, "edit-#{advertisement.reference}"}) {:ok, state} end -then_ ~r/^I should see the updated property details$/, fn state -> - assert visible_in_page?(~r/Updated House/) - assert visible_in_page?(~r/Better house/) - assert visible_in_page?(~r/Paris/) + then_ ~r/^I should not see an edit button$/, fn state -> + assert not visible_in_page? ~r/Edit Property/ {:ok, state} end + + defp setup_session(email, password) do + navigate_to("/login") + fill_field({:id, "email"}, email) + fill_field({:id, "password"}, password) + click({:id, "login_button"}) + end + + # https://stackoverflow.com/a/49861811 + defp select_dropdown(drop_down_id, option) do + find_element(:css, "##{drop_down_id} option[value='#{option}']") |> click() + end end diff --git a/features/property_update.feature b/features/property_update.feature index da09f13d7d91d533d6dc57ea758ef1c08c1d960d..abbcfc6af5e89b3969b4101aa9dc5c4d550ff8a0 100644 --- a/features/property_update.feature +++ b/features/property_update.feature @@ -1,17 +1,48 @@ Feature: Property Update + Scenario: Owner can update their property Given there exists following accounts | name | surname | birth_date | phone_number | email | password | confirm_password | | Existing | Account | 2000-01-01 | 000 | existing.account@gmail.com | password | password | And the following properties exist - | title | description | type | property_type | state | location | room_count | area | floor | floor_count | price | user_email | - | Test Property | Nice house | :sell | :house | :available | London | 3 | 100.0 | 2 | 5 | 500000 | existing.account@gmail.com | + | title | description | type | property_type | state | location | room_count | area | floor | floor_count | price + | Really cool property | Selling this really really house | sell | house | available | London | 3 | 100.0 | 2 | 5 | 500000 And I am logged in And I navigate to my property's details page When I click the edit button And I update the following fields - | title | description | type | property_type | state | location | room_count | area | floor | floor_count | price | - | Updated House | Better house | :rent | :apartment | :available | Paris | 4 | 120.0 | 3 | 6 | 600000 | + | title | description | type | property_type | state | location | room_count | area | floor | price | + | Really AMAZING property | Pls rent this really amazing apartment | rent | apartment | reserved | Paris | 4 | 120.0 | 3 | 600 | And I click save changes Then I should see a success message - And I should see the updated property details \ No newline at end of file + And I should be redirected back to the property page + And I should see the updated property details + + Scenario: Owner cannot update their property when entering invalid details + Given there exists following accounts + | name | surname | birth_date | phone_number | email | password | confirm_password | + | Existing | Account | 2000-01-01 | 000 | existing.account@gmail.com | password | password | + And the following properties exist + | title | description | type | property_type | location | room_count | area | floor | floor_count | price + | Really cool property | Selling this really really house | sell | house | London | 3 | 100.0 | 2 | 5 | 500000 + And I am logged in + And I navigate to my property's details page + When I click the edit button + And I update the following fields + | title | description | type | property_type | state | location | room_count | area | floor | price | + | Too short | Too short | rent | house | available | Paris | 4 | -120.0 | 3 | -600 | + And I click save changes + Then I should see error message + And I should see the edit page again + + Scenario: A random user cannot update a property + Given there exists following accounts + | name | surname | birth_date | phone_number | email | password | confirm_password | + | Existing | Account | 2000-01-01 | 000 | existing.account@gmail.com | password | password | + | Random | Account | 2000-01-01 | 000 | random.account@gmail.com | password | password | + And the following properties exist + | title | description | type | property_type | state | location | room_count | area | floor | floor_count | price + | Really cool property | Selling this really really house | sell | house | available | London | 3 | 100.0 | 2 | 5 | 500000 + And I am logged in as a random user who does not own the advertisement + And I navigate to the property's details page + Then I should not see an edit button diff --git a/lib/proptrackr_web/controllers/properties_html/edit.html.heex b/lib/proptrackr_web/controllers/properties_html/edit.html.heex index 80d2927e776142dd01e69c3001b92011e5be469f..1970b8902b89347890617124082387669bc5ba68 100644 --- a/lib/proptrackr_web/controllers/properties_html/edit.html.heex +++ b/lib/proptrackr_web/controllers/properties_html/edit.html.heex @@ -13,17 +13,17 @@ method="put" action={~p"/properties/#{@property.reference}"} > - <.input field={f[:title]} id="property_title" type="text" label="Title" required /> + <.input field={f[:title]} id="title" type="text" label="Title" required /> <.input field={f[:description]} - id="property_description" + id="description" type="textarea" label="Description" required /> <.input field={f[:type]} - id="property_type" + id="type" type="select" label="Type" options={[:rent, :sell]} @@ -31,7 +31,7 @@ /> <.input field={f[:property_type]} - id="property_property_type" + id="property_type" type="select" label="Property Type" options={[:apartment, :house, :other]} @@ -39,40 +39,40 @@ /> <.input field={f[:state]} - id="property_state" + id="state" type="select" label="State" options={[:available, :reserved, :unavailable]} required /> - <.input field={f[:location]} id="property_location" type="text" label="Location" required /> + <.input field={f[:location]} id="location" type="text" label="Location" required /> <.input field={f[:room_count]} - id="property_room_count" + id="room_count" type="number" label="Room Count" required /> <.input field={f[:area]} - id="property_area" + id="area" type="number" step="0.01" label="Area (m²)" required /> - <.input field={f[:floor]} id="property_floor" type="number" label="Floor" required /> + <.input field={f[:floor]} id="floor" type="number" label="Floor" required /> <.input field={f[:floor_count]} - id="property_floor_count" + id="floor_count" type="number" label="Total Floors" required /> - <.input field={f[:price]} id="property_price" type="number" step="0.01" label="Price" required /> + <.input field={f[:price]} id="price" type="number" step="0.01" label="Price" required /> <:actions> - <.button id="save-changes-button">Save Changes</.button> + <.button id="save-changes">Save Changes</.button> <.link href={~p"/properties/#{@property.reference}"} class="ml-4"> Cancel </.link> diff --git a/lib/proptrackr_web/controllers/properties_html/index.html.heex b/lib/proptrackr_web/controllers/properties_html/index.html.heex index 87e1b99ffb84bed24c1491db9c09ae16a004bede..4b7bdb72c0d21a9de917b2c5c626e00681e8b075 100644 --- a/lib/proptrackr_web/controllers/properties_html/index.html.heex +++ b/lib/proptrackr_web/controllers/properties_html/index.html.heex @@ -29,9 +29,8 @@ </div> <div class="flex flex-row justify-end"> - <!-- TODO: Implement linking --> <.link href={~p"/properties/#{property.reference}"}> - <.button type="button" class="text-white rounded px-4 py-2">View more</.button> + <.button type="button" class="text-white rounded px-4 py-2" id={ "edit-#{property.reference}" }>View more</.button> </.link> </div> </div> diff --git a/lib/proptrackr_web/controllers/properties_html/show.html.heex b/lib/proptrackr_web/controllers/properties_html/show.html.heex index 702d5b5f6e5310fe6804813fbc3137189da2ad6f..c045baddfada33d5bc0408db9dc308ae3cb08bd6 100644 --- a/lib/proptrackr_web/controllers/properties_html/show.html.heex +++ b/lib/proptrackr_web/controllers/properties_html/show.html.heex @@ -6,7 +6,8 @@ <%= if @can_edit do %> <.link href={~p"/properties/#{@property.reference}/edit"} - class="edit-property-button bg-zinc-900 hover:bg-zinc-700 text-white px-4 py-2 rounded-md mr-4" + class="bg-zinc-900 hover:bg-zinc-700 text-white px-4 py-2 rounded-md mr-4" + id="edit-property" > Edit Property </.link> @@ -80,4 +81,4 @@ <p class="text-sm text-gray-600">Email: <%= @property.user.email %></p> </div> </div> -</div> \ No newline at end of file +</div>