Skip to content
Snippets Groups Projects
Commit 84bb5e80 authored by shyngys's avatar shyngys Committed by maripuus
Browse files

Resolve "implement photo update and delete"

parent 5cbf14f0
No related branches found
No related tags found
1 merge request!40Resolve "implement photo update and delete"
......@@ -70,7 +70,6 @@ defmodule AdvertisementCreationContext do
end
and_ ~r/^I upload 3 valid photos$/, fn state ->
# Find the file input element
file_input = find_element(:id, "property_photos")
execute_script("""
......@@ -88,7 +87,6 @@ defmodule AdvertisementCreationContext do
})();
""")
# Give the browser a moment to process the file upload
:timer.sleep(100)
{:ok, state}
......@@ -113,24 +111,19 @@ defmodule AdvertisementCreationContext do
end
and_ ~r/^I should see all uploaded photos on the details page$/, fn state ->
# Wait for images to load
:timer.sleep(500)
# Find the photo slides container and count the photos
photo_slides = find_all_elements(:css, ".photo-slide")
# Verify navigation elements if there are multiple photos
if length(photo_slides) > 1 do
prev_button = find_element(:css, "button.prev-photo")
next_button = find_element(:css, "button.next-photo")
thumbnails = find_all_elements(:css, "button.thumbnail")
# Check that we have the correct number of thumbnails
assert length(thumbnails) == length(photo_slides),
"Expected #{length(photo_slides)} thumbnails but found #{length(thumbnails)}"
end
# Verify each photo is loaded and visible
Enum.each(photo_slides, fn slide ->
img = find_within_element(slide, :css, "img")
src = attribute_value(img, "src")
......@@ -139,12 +132,10 @@ defmodule AdvertisementCreationContext do
"Image source should contain /uploads/ but was #{src}"
end)
# Check counter display
counter = find_element(:css, ".current-photo")
counter_text = visible_text(counter)
assert counter_text == "1", "Counter should start at 1"
# Verify total number of photos
assert length(photo_slides) == 3,
"Expected 3 photos but found #{length(photo_slides)}"
......@@ -153,26 +144,20 @@ defmodule AdvertisementCreationContext do
and_ ~r/^I should see the first photo on the homepage$/, fn state ->
# Navigate to homepage
navigate_to("/")
:timer.sleep(500) # Wait for page to load
:timer.sleep(500)
# Find the property container
property_div = find_element(:css, "#properties > div:first-child")
# Find the photo container within the property
photo_container = find_within_element(property_div, :css, ".w-48.h-48")
# Check if there's an image
case find_all_within_element(photo_container, :css, "img") do
[img | _] ->
src = attribute_value(img, "src")
# Verify image source contains uploads path
assert String.contains?(src, "/uploads/"),
"Image source should contain /uploads/ but was #{src}"
# Verify image has proper styling
classes = attribute_value(img, "class")
assert String.contains?(classes, "object-cover"),
......@@ -180,7 +165,6 @@ defmodule AdvertisementCreationContext do
assert String.contains?(classes, "rounded"),
"Image should have rounded class"
# Verify container dimensions
container_classes = attribute_value(photo_container, "class")
assert String.contains?(container_classes, "w-48"),
......@@ -192,8 +176,6 @@ defmodule AdvertisementCreationContext do
flunk("No image found in the photo container")
end
IO.puts("Homepage photo verification completed")
{:ok, state}
end
......
......@@ -156,6 +156,21 @@ defmodule MyAdvertisementsContext do
assert visible_in_page? ~r/#{user.surname}/
assert visible_in_page? ~r/#{user.phone_number}/
assert visible_in_page? ~r/#{user.email}/
user = Repo.get_by(User, id: advertisement.user_id)
assert visible_in_page? ~r/#{user.name}/
assert visible_in_page? ~r/#{user.surname}/
assert visible_in_page? ~r/#{user.phone_number}/
assert visible_in_page? ~r/#{user.email}/
property_with_photos = Repo.preload(advertisement, :photos)
Enum.each(property_with_photos.photos, fn photo ->
assert find_element(:css, "img[src*='#{photo.filename}']")
|> has_class?("w-full")
assert find_element(:css, "button.thumbnail img[src*='#{photo.filename}']")
end)
{:ok, state}
end
......
This diff is collapsed.
......@@ -12,23 +12,11 @@
for={@changeset}
method="put"
action={~p"/properties/#{@property.reference}"}
multipart
>
<.input field={f[:title]} id="title" type="text" label="Title" required />
<.input
field={f[:description]}
id="description"
type="textarea"
label="Description"
required
/>
<.input
field={f[:type]}
id="type"
type="select"
label="Type"
options={[:rent, :sell]}
required
/>
<.input field={f[:description]} id="description" type="textarea" label="Description" required />
<.input field={f[:type]} id="type" type="select" label="Type" options={[:rent, :sell]} required />
<.input
field={f[:property_type]}
id="property_type"
......@@ -46,31 +34,37 @@
required
/>
<.input field={f[:location]} id="location" type="text" label="Location" required />
<.input
field={f[:room_count]}
id="room_count"
type="number"
label="Room Count"
required
/>
<.input
field={f[:area]}
id="area"
type="number"
step="0.01"
label="Area (m²)"
required
/>
<.input field={f[:room_count]} id="room_count" type="number" label="Room Count" required />
<.input field={f[:area]} id="area" type="number" step="0.01" label="Area (m²)" required />
<.input field={f[:floor]} id="floor" type="number" label="Floor" required />
<.input
field={f[:floor_count]}
id="floor_count"
type="number"
label="Total Floors"
required
/>
<.input field={f[:floor_count]} id="floor_count" type="number" label="Total Floors" required />
<.input field={f[:price]} id="price" type="number" step="0.01" label="Price" required />
<div class="mt-4">
<label class="block text-sm font-medium text-gray-700">Current Photos</label>
<div class="grid grid-cols-3 gap-4 mt-2">
<%= for photo <- @property.photos do %>
<div class="relative">
<img src={~p"/uploads/#{photo.filename}"} class="w-32 h-32 object-cover rounded" />
<label class="mt-1 block">
<input type="checkbox" name="property[photos_to_delete][]" value={photo.id} /> Delete
</label>
</div>
<% end %>
</div>
</div>
<div class="mt-4">
<label class="block text-sm font-medium text-gray-700">Add New Photos</label>
<input
type="file"
name="property[photos][]"
multiple
accept="image/*"
class="mt-1 block w-full"
/>
</div>
<:actions>
<.button id="save-changes">Save Changes</.button>
<.link href={~p"/properties/#{@property.reference}"} class="ml-4">
......
......@@ -361,6 +361,83 @@ defmodule PropTrackrWeb.PropertiesControllerTest do
assert get_flash(conn, :error) == "Property not found."
end
test "owner can update property and add new photos", %{conn: conn, user: user, test_photo: test_photo} do
property = %Property{
reference: Ecto.UUID.generate(),
user_id: user.id
}
|> Property.changeset(@valid_data)
|> Repo.insert!()
# Add initial photo
photo = %Photo{
property_id: property.id,
filename: "initial.jpg",
order: 1
}
|> Repo.insert!()
# Create test file
File.write!(Path.join(Application.app_dir(:proptrackr, "priv/static/uploads"), "initial.jpg"), "initial content")
conn = conn |> setup_session(user)
update_params = Map.put(@update_data, "photos", [test_photo])
conn = put(conn, ~p"/properties/#{property.reference}", property: update_params)
assert redirected_to(conn) == ~p"/properties/#{property.reference}"
updated_property = Repo.get_by!(Property, reference: property.reference) |> Repo.preload(:photos)
assert length(updated_property.photos) == 2
assert Enum.all?(updated_property.photos, fn photo ->
File.exists?(Path.join(Application.app_dir(:proptrackr, "priv/static/uploads"), photo.filename))
end)
end
test "owner can update property and delete photos", %{conn: conn, user: user} do
property = %Property{
reference: Ecto.UUID.generate(),
user_id: user.id
}
|> Property.changeset(@valid_data)
|> Repo.insert!()
# Add two photos
photo1 = %Photo{
property_id: property.id,
filename: "photo1.jpg",
order: 1
}
|> Repo.insert!()
photo2 = %Photo{
property_id: property.id,
filename: "photo2.jpg",
order: 2
}
|> Repo.insert!()
# Create test files
uploads_dir = Application.app_dir(:proptrackr, "priv/static/uploads")
File.write!(Path.join(uploads_dir, "photo1.jpg"), "test1")
File.write!(Path.join(uploads_dir, "photo2.jpg"), "test2")
conn = conn |> setup_session(user)
update_params = Map.put(@update_data, "photos_to_delete", [photo1.id])
conn = put(conn, ~p"/properties/#{property.reference}", property: update_params)
assert redirected_to(conn) == ~p"/properties/#{property.reference}"
updated_property = Repo.get_by!(Property, reference: property.reference) |> Repo.preload(:photos)
assert length(updated_property.photos) == 1
# Verify first photo was deleted
assert not File.exists?(Path.join(uploads_dir, "photo1.jpg"))
# Verify second photo still exists
assert File.exists?(Path.join(uploads_dir, "photo2.jpg"))
end
#DELETE TESTS
test "owner can delete their property", %{conn: conn, user: user} do
property = %Property{
......@@ -424,6 +501,47 @@ defmodule PropTrackrWeb.PropertiesControllerTest do
assert Repo.get_by(Property, reference: property.reference) != nil
end
test "deleting property removes all associated photos", %{conn: conn, user: user} do
property = %Property{
reference: Ecto.UUID.generate(),
user_id: user.id
}
|> Property.changeset(@valid_data)
|> Repo.insert!()
# Add photos
uploads_dir = Application.app_dir(:proptrackr, "priv/static/uploads")
Enum.each(1..3, fn i ->
filename = "test#{i}.jpg"
File.write!(Path.join(uploads_dir, filename), "test content #{i}")
%Photo{
property_id: property.id,
filename: filename,
order: i
}
|> Repo.insert!()
end)
conn = conn |> setup_session(user)
conn = delete(conn, ~p"/properties/#{property.reference}")
assert redirected_to(conn) == "/me/properties"
assert get_flash(conn, :info) == "Property deleted successfully."
# Verify property deletion
assert Repo.get_by(Property, reference: property.reference) == nil
# Verify photo records deletion
assert Repo.all(from p in Photo, where: p.property_id == ^property.id) == []
# Verify photo files deletion
Enum.each(1..3, fn i ->
assert not File.exists?(Path.join(uploads_dir, "test#{i}.jpg"))
end)
end
# FR17 - Test if property owner details are shown
test "Authenticated user should see the contact details of the property owner", %{conn: conn, user: user} do
......
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