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

Merge branch '29-fr-19-save-last-5-searches' into 'main'

Implement tests for FR-19 and FR-20

Closes #29 and #30

See merge request !35
parents 5a4330f3 852d932a
No related branches found
No related tags found
1 merge request!35Implement tests for FR-19 and FR-20
Pipeline #46639 failed
......@@ -29,6 +29,7 @@ defmodule PropertySearchContext do
:ok,
state
|> Map.put(:email, existing_user[:email])
|> Map.put(:password, existing_user[:password])
}
end
......@@ -144,4 +145,68 @@ defmodule PropertySearchContext do
{:ok, state}
end
and_ ~r/^I am logged in$/, fn state ->
setup_session(state[:email], state[:password])
{:ok, state}
end
and_ ~r/^I want to search$/, fn state ->
navigate_to("/")
click({:id, "search"})
{:ok, state}
end
and_ ~r/^I perform a search$/, fn state ->
click({:id, "search_button"})
{:ok, state}
end
then_ ~r/^I should see a saved search query$/, fn state ->
assert visible_in_page? ~r/Previous searches/
el_count = length(find_all_elements(:class, "previous-search"))
assert el_count == 1
{:ok, state}
end
and_ ~r/^I go to the homepage$/, fn state ->
navigate_to("/")
{:ok, state}
end
then_ ~r/^I should not see a search button$/, fn state ->
refute visible_in_page? ~r/Search/
els = find_all_elements(:id, "search_button")
assert length(els) == 0
{:ok, state}
end
and_ ~r/^I go to the search URL$/, fn state ->
navigate_to("/search")
{: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 click on a previous search$/, fn state ->
click({:class, "previous-search-link"})
{:ok, state}
end
then_ ~r/^I should see the properties from the previous search$/, fn state ->
advertisements = state[:advertisements]
el_count = length(find_all_elements(:class, "property-card"))
assert el_count == length(advertisements)
{: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
Feature: FR-13, FR-19 & FR-20: Property Search and Revisit
Scenario: User should see filter options and boundaries for search
Scenario: 3.2, 3.3 Authenticated user should see filter options and boundaries for search
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 |
......@@ -10,6 +10,7 @@ Feature: FR-13, FR-19 & FR-20: Property Search and Revisit
| Rent property | Also a really cool property | rent | apartment | available | London, UK | 3 | 160.0 | 2 | 5 | 6000 |
| Rent property Tartu | Also a really cool property | rent | apartment | available | Tartu, Estonia | 2 | 120.0 | 1 | 2 | 800 |
| Buy property Tartu | Also a really cool property | sell | apartment | available | Tartu, Estonia | 2 | 120.0 | 1 | 2 | 800000 |
And I am logged in
And I want to perform a search
Then I should see all countries of the properties in the filter options
And I should see all cities of the properties in the filter options
......@@ -18,7 +19,7 @@ Feature: FR-13, FR-19 & FR-20: Property Search and Revisit
And I should see a max room filter with the room count of the properties
And I should see a min room filter with the room count of the properties
Scenario: User should be able to search with default values
Scenario: 3.2, 3.3 Authenticated user should be able to search with default values
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 |
......@@ -28,6 +29,48 @@ Feature: FR-13, FR-19 & FR-20: Property Search and Revisit
| Rent property | Also a really cool property | rent | apartment | available | London, UK | 3 | 160.0 | 2 | 5 | 6000 |
| Rent property Tartu | Also a really cool property | rent | apartment | available | Tartu, Estonia | 2 | 120.0 | 1 | 2 | 800 |
| Buy property Tartu | Also a really cool property | sell | apartment | available | Tartu, Estonia | 2 | 120.0 | 1 | 2 | 800000 |
And I am logged in
And I want to perform a search
When I click on the search button
Then I should see "3" properties
Scenario: 3.2 Unauthenticated user should not see a search button
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 I go to the homepage
Then I should not see a search button
Scenario: 3.2 Unauthenticated user should be redirected to login when visiting search URL
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 I go to the search URL
Then I should be redirected to the login page
Scenario: 3.2 Authenticated users should see saved search queries
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 |
| Rent property | Also a really cool property | rent | apartment | available | London, UK | 3 | 160.0 | 2 | 5 | 6000 |
And I am logged in
And I want to search
And I perform a search
And I want to search
Then I should see a saved search query
Scenario: 4.4 Authenticated user can revisit their previous searches
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 |
| Rent property | Also a really cool property | rent | apartment | available | London, UK | 3 | 160.0 | 2 | 5 | 6000 |
And I am logged in
And I want to search
And I perform a search
And I want to search
And I click on a previous search
Then I should see the properties from the previous search
......@@ -77,6 +77,7 @@ defmodule PropTrackr.Search do
|> validate_required([:type, :location, :min_price, :max_price, :min_rooms, :max_rooms])
|> validate_minmax(:min_price, :max_price)
|> validate_minmax(:min_rooms, :max_rooms)
|> cast_areas()
end
defp validate_minmax(changeset, min_key, max_key) do
......@@ -89,4 +90,13 @@ defmodule PropTrackr.Search do
true -> changeset
end
end
defp cast_areas(changeset) do
areas = get_field(changeset, :areas)
if areas == nil do
put_change(changeset, :areas, [])
else
changeset
end
end
end
......@@ -16,7 +16,7 @@
</.link>
<% end %>
<%= if not @is_search do %>
<%= if not @is_search and @conn.assigns[:current_user] do %>
<.link href={~p"/search"}>
<.button type="button" class="text-white rounded px-4 py-2" id="search">
Search
......
......@@ -10,100 +10,114 @@ defmodule PropTrackrWeb.SearchController do
alias PropTrackr.NotInterested
def index(conn, _params) do
min_price = from(p in Property, select: min(p.price)) |> Repo.one
max_price = from(p in Property, select: max(p.price)) |> Repo.one
current_user = conn.assigns.current_user
min_rooms = 0
max_rooms = from(p in Property, select: max(p.room_count)) |> Repo.one
if current_user do
min_price = from(p in Property, select: min(p.price)) |> Repo.one
max_price = from(p in Property, select: max(p.price)) |> Repo.one
locations = from(p in Property, select: p.location) |> Repo.all
cities = Enum.map(locations, fn location -> String.split(location, ",") |> hd |> String.trim() end)
cities = Enum.uniq(cities)
countries = Enum.map(locations, fn location -> String.split(location, ",") |> tl |> hd |> String.trim() end)
countries = Enum.uniq(countries)
min_rooms = 0
max_rooms = from(p in Property, select: max(p.room_count)) |> Repo.one
changeset = Search.changeset(%Search{})
locations = from(p in Property, select: p.location) |> Repo.all
cities = Enum.map(locations, fn location -> String.split(location, ",") |> hd |> String.trim() end)
cities = Enum.uniq(cities)
countries = Enum.map(locations, fn location -> String.split(location, ",") |> tl |> hd |> String.trim() end)
countries = Enum.uniq(countries)
current_user = conn.assigns.current_user
previous_searches = case current_user do
nil -> []
_ ->
Repo.all(
from s in Search,
where: s.user_id == ^current_user.id,
order_by: [desc: s.inserted_at]
)
end
changeset = Search.changeset(%Search{})
current_user = conn.assigns.current_user
previous_searches = case current_user do
nil -> []
_ ->
Repo.all(
from s in Search,
where: s.user_id == ^current_user.id,
order_by: [desc: s.inserted_at]
)
end
render(
conn, "search.html",
changeset: changeset,
min_price: min_price,
max_price: max_price,
min_rooms: min_rooms,
max_rooms: max_rooms,
cities: cities,
countries: countries,
previous_searches: previous_searches
)
render(
conn, "search.html",
changeset: changeset,
min_price: min_price,
max_price: max_price,
min_rooms: min_rooms,
max_rooms: max_rooms,
cities: cities,
countries: countries,
previous_searches: previous_searches
)
else
conn
|> put_flash(:error, "You are not logged in")
|> redirect(to: "/login")
end
end
def create(conn, %{ "search" => search }) do
current_user = conn.assigns.current_user
changeset = Search.changeset(%Search{}, search)
query = Search.create_query(changeset.changes, current_user)
if current_user do
changeset = Search.changeset(%Search{}, search)
query = Search.create_query(changeset.changes, current_user)
saved = if current_user do
searches = Repo.all(
from s in Search,
where: s.user_id == ^current_user.id,
order_by: [asc: s.inserted_at]
)
saved = if current_user do
searches = Repo.all(
from s in Search,
where: s.user_id == ^current_user.id,
order_by: [asc: s.inserted_at]
)
if length(searches) >= 5 do
case Repo.delete(Enum.at(searches, 0)) do
{:ok, _search} -> :ok
{:error, _changeset} -> :error
end
end
search_assoc = Ecto.build_assoc(current_user, :searches, changeset.changes)
search_changeset = Search.changeset(search_assoc, changeset.changes)
if length(searches) >= 5 do
case Repo.delete(Enum.at(searches, 0)) do
case Repo.insert(search_changeset) do
{:ok, _search} -> :ok
{:error, _changeset} -> :error
end
else
:ok
end
search_assoc = Ecto.build_assoc(current_user, :searches, changeset.changes)
search_changeset = Search.changeset(search_assoc, changeset.changes)
case Repo.insert(search_changeset) do
{:ok, _search} -> :ok
{:error, _changeset} -> :error
conn = case saved do
:ok -> conn
:error -> conn
|> put_flash(:error, "Unknown error saving search")
end
else
:ok
end
conn = case saved do
:ok -> conn
:error -> conn
|> put_flash(:error, "Unknown error saving search")
end
properties = query |> Repo.all()
properties = query |> Repo.all()
favorites = case conn.assigns.current_user do
nil -> []
current_user ->
Repo.all(
from f in Favorite,
where: f.user_id == ^current_user.id,
select: f.property_id
)
end
favorites = case conn.assigns.current_user do
nil -> []
current_user ->
Repo.all(
from f in Favorite,
where: f.user_id == ^current_user.id,
select: f.property_id
)
render(
conn, "index.html",
properties: properties,
header_text: "Search results",
is_search: true,
favorites: favorites
)
else
conn
|> put_flash(:error, "You are not logged in")
|> redirect(to: "/login")
end
render(
conn, "index.html",
properties: properties,
header_text: "Search results",
is_search: true,
favorites: favorites
)
end
def show(conn, %{ "id" => id }) do
......@@ -120,7 +134,7 @@ defmodule PropTrackrWeb.SearchController do
if search == nil do
conn
|> put_flash(:error, "Search not found")
|> redirect(to: ~p"/searches")
|> redirect(to: ~p"/search")
else
search = Search.changeset(search)
query = Search.create_query(search.data, current_user)
......
......@@ -12,8 +12,8 @@
<h3 class="font-bold">Previous searches</h3>
<ul>
<%= for search <- @previous_searches do %>
<li>
<.link navigate={"/search/#{search.id}"} class="underline">
<li class="previous-search">
<.link navigate={"/search/#{search.id}"} class="underline previous-search-link">
<%= "#{search.type} in #{search.location} with #{Enum.join(search.areas, ", ")} areas, #{search.min_price} - #{search.max_price} € price range and #{search.min_rooms} - #{search.max_rooms} rooms" %>
</.link>
</li>
......
......@@ -55,8 +55,6 @@ defmodule PropTrackrWeb.FavoriteControllerTest do
conn = conn |> setup_session(user)
conn = post(conn, ~p"/api/properties/#{property.reference}/favorite")
IO.inspect(conn)
assert json_response(conn, 200)["message"] == "Property added to favorites"
favorite = Repo.one(from f in Favorite,
......
......@@ -3,6 +3,8 @@ defmodule PropTrackrWeb.SearchControllerTest do
alias PropTrackr.Accounts.User
alias PropTrackr.Properties.Property
alias PropTrackr.Repo
alias PropTrackr.Search
import Ecto.Query, only: [from: 2]
setup do
user = %User{
......@@ -17,6 +19,18 @@ defmodule PropTrackrWeb.SearchControllerTest do
}
user = Repo.insert!(user)
another_user = %User{
name: "Another user",
surname: "User",
birth_date: "2000-01-01",
phone_number: "000",
bio: "Yo",
email: "another.user@gmail.com",
password: "testing",
confirm_password: "testing",
}
another_user = Repo.insert!(another_user)
properties = [
%Property{
title: "Rent property cheap",
......@@ -86,10 +100,12 @@ defmodule PropTrackrWeb.SearchControllerTest do
]
Enum.each(properties, fn property -> Repo.insert!(property) end)
{:ok, %{user: user, properties: properties}}
{:ok, %{user: user, another_user: another_user, properties: properties}}
end
test "Searching with boundaries should show results within the boundaries", %{ conn: conn, user: user, properties: properties } do
test "3.2, 3.3 Searching with boundaries should show results within the boundaries for authenticated user", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
properties = Repo.all(Property)
conn = post(conn, ~p"/search", %{search: %{
......@@ -118,7 +134,9 @@ defmodule PropTrackrWeb.SearchControllerTest do
end)
end
test "Searching with multiple areas should show results within the areas", %{ conn: conn, user: user, properties: properties } do
test "3.2, 3.3 Searching with multiple areas should show results within the areas for authenticated user", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
properties = Repo.all(Property)
conn = post(conn, ~p"/search", %{search: %{
......@@ -147,7 +165,9 @@ defmodule PropTrackrWeb.SearchControllerTest do
end)
end
test "Searching with type filter rent should show proper results", %{ conn: conn, user: user, properties: properties } do
test "3.2, 3.3 Searching with type filter rent should show proper results for authenticated user", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
properties = Repo.all(Property)
conn = post(conn, ~p"/search", %{search: %{
......@@ -176,7 +196,9 @@ defmodule PropTrackrWeb.SearchControllerTest do
end)
end
test "Searching with type filter sell should show proper results", %{ conn: conn, user: user, properties: properties } do
test "3.2, 3.3 Searching with type filter sell should show proper results for authenticated user", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
properties = Repo.all(Property)
conn = post(conn, ~p"/search", %{search: %{
......@@ -205,7 +227,9 @@ defmodule PropTrackrWeb.SearchControllerTest do
end)
end
test "Searching outside of the price range should show no results", %{ conn: conn, user: user, properties: properties } do
test "3.2, 3.3 Searching outside of the price range should show no results for authenticated user", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
properties = Repo.all(Property)
conn = post(conn, ~p"/search", %{search: %{
......@@ -222,7 +246,9 @@ defmodule PropTrackrWeb.SearchControllerTest do
assert result =~ "No advertisements at the moment"
end
test "Searching outside of the room range should show no results", %{ conn: conn, user: user, properties: properties } do
test "3.2, 3.3 Searching outside of the room range should show no results for authenticated user", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
properties = Repo.all(Property)
conn = post(conn, ~p"/search", %{search: %{
......@@ -239,7 +265,9 @@ defmodule PropTrackrWeb.SearchControllerTest do
assert result =~ "No advertisements at the moment"
end
test "Searching within correct price range should show proper results", %{ conn: conn, user: user, properties: properties } do
test "3.2, 3.3 Searching within correct price range should show proper results for authenticated user", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
properties = Repo.all(Property)
min_price = 100
......@@ -271,7 +299,9 @@ defmodule PropTrackrWeb.SearchControllerTest do
end)
end
test "Searching within correct room count range should show proper results", %{ conn: conn, user: user, properties: properties } do
test "3.2, 3.3 Searching within correct room count range should show proper results for authenticated user", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
properties = Repo.all(Property)
min_rooms = 1
......@@ -303,7 +333,9 @@ defmodule PropTrackrWeb.SearchControllerTest do
end)
end
test "Searching without any areas should show all results", %{ conn: conn, user: user, properties: properties } do
test "3.2, 3.3 Searching without any areas should show all results for authenticated user", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
properties = Repo.all(Property)
conn = post(conn, ~p"/search", %{search: %{
......@@ -324,7 +356,9 @@ defmodule PropTrackrWeb.SearchControllerTest do
end)
end
test "Searching with non-existent with type but existing location should show no results", %{ conn: conn, user: user, properties: properties } do
test "3.2, 3.3 Searching with non-existent with type but existing location should show no results for authenticated user", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
properties = Repo.all(Property)
conn = post(conn, ~p"/search", %{search: %{
......@@ -341,4 +375,176 @@ defmodule PropTrackrWeb.SearchControllerTest do
assert result =~ "No advertisements at the moment"
end
test "Unauthenticated user should not be able to use the advanced search feature", %{ conn: conn, user: user, properties: properties } do
conn = post(conn, ~p"/search", %{search: %{
type: :any,
location: "Estonia",
areas: ["Tartu", "Tallinn"],
min_price: 100.0,
max_price: 10000000.0,
min_rooms: 1,
max_rooms: 100,
}})
assert redirected_to(conn) == "/login"
conn = get conn, redirected_to(conn)
assert get_flash(conn, :error) =~ ~r/You are not logged in/
end
test "4.4 The last 5 searches of authenticated user should be saved", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
searches = Repo.all(from s in Search, where: s.user_id == ^user.id, order_by: [asc: s.inserted_at])
assert length(searches) == 0
conn = post(conn, ~p"/search", %{search: %{
type: :any,
location: "Estonia",
areas: ["Tartu", "Tallinn"],
min_price: 100.0,
max_price: 10000000.0,
min_rooms: 1,
max_rooms: 100,
}})
conn = post(conn, ~p"/search", %{search: %{
type: :any,
location: "Estonia",
areas: ["Tartu"],
min_price: 100.0,
max_price: 10000000.0,
min_rooms: 1,
max_rooms: 100,
}})
conn = post(conn, ~p"/search", %{search: %{
type: :any,
location: "Estonia",
areas: ["Tallinn"],
min_price: 100.0,
max_price: 10000000.0,
min_rooms: 1,
max_rooms: 100,
}})
conn = post(conn, ~p"/search", %{search: %{
type: :any,
location: "Estonia",
areas: ["Tartu", "Tallinn"],
min_price: 100.0,
max_price: 10000000.0,
min_rooms: 1,
max_rooms: 100,
}})
conn = post(conn, ~p"/search", %{search: %{
type: :any,
location: "Estonia",
areas: ["Tartu"],
min_price: 100.0,
max_price: 10000000.0,
min_rooms: 1,
max_rooms: 100,
}})
conn = post(conn, ~p"/search", %{search: %{
type: :any,
location: "Estonia",
areas: ["Jõgeva"],
min_price: 100.0,
max_price: 10000000.0,
min_rooms: 1,
max_rooms: 100,
}})
conn = get conn, "/search"
result = html_response(conn, 200)
assert result =~ ~r/Previous searches/
searches = Repo.all(from s in Search, where: s.user_id == ^user.id, order_by: [asc: s.inserted_at])
assert length(searches) == 5
end
test "4.4 Authenticated user should be able to see a previous search by clicking it", %{ conn: conn, user: user, properties: properties } do
conn = conn |> setup_session(user)
conn = post(conn, ~p"/search", %{search: %{
type: :any,
location: "Estonia",
areas: ["Tartu", "Tallinn"],
min_price: 100.0,
max_price: 10000000.0,
min_rooms: 1,
max_rooms: 100,
}})
conn = get conn, "/search"
result = html_response(conn, 200)
assert result =~ ~r/Previous searches/
searches = Repo.all(from s in Search, where: s.user_id == ^user.id, order_by: [asc: s.inserted_at])
assert length(searches) == 1
conn = get conn, "/search/#{Enum.at(searches, 0).id}"
result = html_response(conn, 200)
assert result =~ ~r/Search results/
end
test "4.4 Authenticated user should not see another user's search when clicking on it", %{ conn: conn, user: user, another_user: another_user, properties: properties } do
conn = conn |> setup_session(user)
conn = post(conn, ~p"/search", %{search: %{
type: :any,
location: "Estonia",
areas: ["Tartu", "Tallinn"],
min_price: 100.0,
max_price: 10000000.0,
min_rooms: 1,
max_rooms: 100,
}})
conn = get conn, "/search"
result = html_response(conn, 200)
assert result =~ ~r/Previous searches/
searches = Repo.all(from s in Search, where: s.user_id == ^user.id, order_by: [asc: s.inserted_at])
assert length(searches) == 1
conn = get conn, "/search/#{Enum.at(searches, 0).id}"
result = html_response(conn, 200)
assert result =~ ~r/Search results/
conn = conn |> setup_session(another_user)
conn = get conn, "/search"
result = html_response(conn, 200)
refute result =~ ~r/Previous searches/
# Note: This should be 0 since the user does not have any searches
searches = Repo.all(from s in Search, where: s.user_id == ^another_user.id, order_by: [asc: s.inserted_at])
assert length(searches) == 0
# Note: Now checking if the user can see another user's search
searches = Repo.all(from s in Search, order_by: [asc: s.inserted_at])
assert length(searches) == 1
conn = get conn, "/search/#{Enum.at(searches, 0).id}"
assert redirected_to(conn) == "/search"
conn = get conn, redirected_to(conn)
assert get_flash(conn, :error) =~ ~r/Search not found/
end
defp setup_session(conn, user) do
conn = conn |> post("/login", email: user.email, password: user.password)
conn = get conn, redirected_to(conn)
conn
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