diff --git a/features/config.exs b/features/config.exs index d6a5a4e95ac197a4f88aac36b774019753146fb0..83d67286f796901c95f8919ff5768da988c66701 100644 --- a/features/config.exs +++ b/features/config.exs @@ -49,4 +49,8 @@ defmodule WhiteBreadConfig do suite name: "FR-14 Mark Property as Favorite", context: MarkPropertyFavoriteContext, feature_paths: ["features/mark_property_favorite.feature"] + + suite name: "FR-26 Mark Property as not Favorite", + context: MarkPropertyNotFavoriteContext, + feature_paths: ["features/mark_property_not_favorite.feature"] end diff --git a/features/contexts/mark_property_not_favorite_context.exs b/features/contexts/mark_property_not_favorite_context.exs new file mode 100644 index 0000000000000000000000000000000000000000..7474c5e0762eb17a6cdc7e8a4bda58e66a04f906 --- /dev/null +++ b/features/contexts/mark_property_not_favorite_context.exs @@ -0,0 +1,164 @@ +defmodule MarkPropertyNotFavoriteContext do + use WhiteBread.Context + use Hound.Helpers + alias PropTrackr.Accounts + alias PropTrackr.Repo + alias PropTrackr.Accounts.User + alias PropTrackr.Properties.Property + alias PropTrackr.Favorites.Favorite + + scenario_starting_state fn _state -> + Ecto.Adapters.SQL.Sandbox.checkout(PropTrackr.Repo) + Ecto.Adapters.SQL.Sandbox.mode(PropTrackr.Repo, {:shared, self()}) + Hound.start_session() + %{} + end + + scenario_finalize fn _status, _state -> + Ecto.Adapters.SQL.Sandbox.checkin(PropTrackr.Repo) + Hound.end_session() + end + + given_ ~r/^there exists 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) + + random_user = List.first(table) + owner = List.last(table) + + { + :ok, + state + |> Map.put(:random_email, random_user[:email]) + |> Map.put(:random_password, random_user[:password]) + |> Map.put(:owner_email, owner[:email]) + |> Map.put(:owner_password, owner[:password]) + } + end + + and_ ~r/^the following properties exist$/, fn state, %{ table_data: table } -> + owner = Repo.get_by(User, email: state[:owner_email]) + + advertisements = + table + |> Enum.map(fn details -> details |> Map.put(:reference, Ecto.UUID.generate()) end) + |> Enum.map(fn details -> {Ecto.build_assoc(owner, :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 as random user$/, fn state -> + setup_session(state[:random_email], state[:random_password]) + {:ok, state} + end + + and_ ~r/^the property "(?<argument_one>[^"]+)" is in my favorites$/, + fn state, %{argument_one: argument_one} -> + current_user = Repo.get_by(User, email: state[:random_email]) + + property = Enum.find(state.advertisements, fn advert -> + advert.title == argument_one + end) + + favorite_changeset = %Favorite{} + |> Favorite.changeset(%{ + user_id: current_user.id, + property_id: property.id + }) + + Repo.insert!(favorite_changeset) + + {:ok, state} + end + + when_ ~r/^I visit the home page$/, fn state -> + navigate_to("/") + {:ok, state} + end + + when_ ~r/^I visit the details page for "(?<argument_one>[^"]+)"$/, + fn state, %{argument_one: argument_one} -> + property = Enum.find(state.advertisements, fn advert -> + advert.title == argument_one + end) + + navigate_to("/properties/#{property.reference}") + :timer.sleep(500) + + {:ok, state} + end + + and_ ~r/^I click the favorite button$/, fn state -> + button = find_element(:css, ".favorite-button") + click(button) + :timer.sleep(500) + + reference = attribute_value(button, "data-property-reference") + + property = Enum.find(state.advertisements, fn advert -> + advert.reference == reference + end) + + {:ok, state |> Map.put(:last_favorited_title, property.title)} + end + + + and_ ~r/^I click the favorite button for "(?<argument_one>[^"]+)"$/, + fn state, %{argument_one: argument_one} -> + property = Enum.find(state.advertisements, fn advert -> + advert.title == argument_one + end) + + button = find_element(:css, ".favorite-button[data-property-reference='#{property.reference}']") + click(button) + :timer.sleep(500) + + {:ok, state |> Map.put(:last_favorited_title, argument_one)} + end + + then_ ~r/^I should see Property removed from favorites message$/, fn state -> + assert visible_in_page? ~r/Property removed from favorites/ + {:ok, state} + end + + then_ ~r/^the property should be marked as not favorite$/, fn state -> + property = case state do + # When we've just unfavorited a property + %{last_favorited_title: title} -> + Enum.find(state.advertisements, fn advert -> + advert.title == title + end) + + # When checking a property that was never favorited + _ -> + List.first(state.advertisements) + end + + button = find_element(:css, ".favorite-button[data-property-reference='#{property.reference}']") + + empty_star = "☆" + button_text = visible_text(button) + assert button_text == empty_star, "Expected empty star (#{empty_star}) but got #{button_text}" + + favorited_attr = attribute_value(button, "data-favorited") + assert favorited_attr == "false", "Expected data-favorited to be 'false' but got #{favorited_attr}" + + {: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 +end diff --git a/features/mark_property_not_favorite.feature b/features/mark_property_not_favorite.feature new file mode 100644 index 0000000000000000000000000000000000000000..523fa13c26fd5fd00d320b3a80aac5bbd2e331a6 --- /dev/null +++ b/features/mark_property_not_favorite.feature @@ -0,0 +1,43 @@ +Feature: Remove Property from Favorites + + Scenario: User can remove a favorite property from home page + 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 | + | Property | Owner | 2000-01-01 | 111 | property.owner@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 random user + And the property "Really cool property" is in my favorites + When I visit the home page + And I click the favorite button for "Really cool property" + Then I should see Property removed from favorites message + And the property should be marked as not favorite + + Scenario: User can remove a favorite property from property details page + 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 | + | Property | Owner | 2000-01-01 | 111 | property.owner@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 random user + And the property "Really cool property" is in my favorites + When I visit the details page for "Really cool property" + And I click the favorite button + Then I should see Property removed from favorites message + And the property should be marked as not favorite + + Scenario: User cannot remove a non-favorited 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 | + | Property | Owner | 2000-01-01 | 111 | property.owner@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 random user + When I visit the home page + Then the property should be marked as not favorite \ No newline at end of file diff --git a/test/proptrackr_web/controllers/favorite_controller_test.exs b/test/proptrackr_web/controllers/favorite_controller_test.exs index f1fc3eb0c8eb638db0a93490ad10f1594edfffbe..6d23aa07238d752aa3f882440db76d9b8fd417c8 100644 --- a/test/proptrackr_web/controllers/favorite_controller_test.exs +++ b/test/proptrackr_web/controllers/favorite_controller_test.exs @@ -115,6 +115,62 @@ defmodule PropTrackrWeb.FavoriteControllerTest do assert initial_favorites_count == final_favorites_count end + # MARKING AS NOT FAVORITE TESTS + test "authenticated user can unfavorite a property", %{conn: conn, random_user: user, property: property} do + Repo.insert!(%Favorite{user_id: user.id, property_id: property.id}) + + conn = conn |> setup_session(user) + conn = delete(conn, ~p"/api/properties/#{property.reference}/favorite") + + assert json_response(conn, 200)["message"] == "Property removed from favorites" + + favorite = Repo.one(from f in Favorite, + where: f.user_id == ^user.id and f.property_id == ^property.id) + assert favorite == nil + end + + + test "authenticated user cannot unfavorite non-favorited property", %{conn: conn, random_user: user, property: property} do + conn = conn |> setup_session(user) + conn = delete(conn, ~p"/api/properties/#{property.reference}/favorite") + + assert json_response(conn, 404)["error"] == "Favorite not found" + + # Verify no changes in database + favorites_count = Repo.one(from f in Favorite, select: count(f.id)) + assert favorites_count == 0 + end + + test "unauthenticated user cannot unfavorite a property", %{conn: conn, random_user: user, property: property} do + Repo.insert!(%Favorite{user_id: user.id, property_id: property.id}) + initial_favorites_count = Repo.one(from f in Favorite, select: count(f.id)) + + conn = delete(conn, ~p"/api/properties/#{property.reference}/favorite") + + assert json_response(conn, 401)["error"] == "You must be logged in" + + # Verify no changes in database + final_favorites_count = Repo.one(from f in Favorite, select: count(f.id)) + assert initial_favorites_count == final_favorites_count + end + + test "cannot unfavorite non-existent property", %{conn: conn, random_user: user} do + conn = conn |> setup_session(user) + conn = delete(conn, ~p"/api/properties/non-existent-reference/favorite") + + assert json_response(conn, 404)["error"] == "Property not found" + end + + test "deleting property removes associated favorites", %{conn: conn, random_user: user, property: property} do + Repo.insert!(%Favorite{user_id: user.id, property_id: property.id}) + + Repo.delete!(property) + + favorite = Repo.one(from f in Favorite, + where: f.user_id == ^user.id and f.property_id == ^property.id) + assert favorite == nil + end + defp setup_session(conn, user) do conn = conn |> post("/login", email: user.email, password: user.password) conn = get conn, redirected_to(conn)