berrypod/test/berrypod_web/live/admin/media_test.exs
jamey 847b5f3e5e add admin media library with image management and block picker integration
- Schema: alt, caption, tags fields on images table with metadata changeset
- Context: list_images with filters, find_usages, used_image_ids, delete_with_cleanup
- Admin UI: /admin/media with grid view, upload, filters, detail panel, metadata editing
- Block editor: :image field type for image_text and content_body blocks
- Page renderer: image_id resolution with legacy URL fallback
- Mobile: bottom sheet detail panel with slide-up animation
- CSS: uses correct --t-* admin theme tokens, admin-badge colour variants

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 22:20:51 +00:00

155 lines
4.7 KiB
Elixir

defmodule BerrypodWeb.Admin.MediaTest do
use BerrypodWeb.ConnCase, async: false
import Phoenix.LiveViewTest
import Berrypod.AccountsFixtures
alias Berrypod.Media
@svg_attrs %{
image_type: "logo",
filename: "logo.svg",
content_type: "image/svg+xml",
file_size: 512,
data: "<svg xmlns=\"http://www.w3.org/2000/svg\"><circle r=\"10\"/></svg>"
}
@raster_attrs %{
image_type: "media",
filename: "banner.png",
content_type: "image/png",
file_size: 1024,
data: <<137, 80, 78, 71>>,
alt: "A banner image"
}
setup do
user = user_fixture()
%{user: user}
end
describe "media library page" do
setup %{conn: conn, user: user} do
%{conn: log_in_user(conn, user)}
end
test "renders media library for admin", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/admin/media")
assert html =~ "Media"
assert html =~ "Upload image"
end
test "shows images in grid", %{conn: conn} do
{:ok, _} = Media.upload_image(@svg_attrs)
{:ok, _} = Media.upload_image(@raster_attrs)
{:ok, _view, html} = live(conn, ~p"/admin/media")
assert html =~ "logo.svg"
assert html =~ "banner.png"
end
test "filters by type", %{conn: conn} do
{:ok, _} = Media.upload_image(@svg_attrs)
{:ok, _} = Media.upload_image(@raster_attrs)
{:ok, view, _html} = live(conn, ~p"/admin/media")
html = render_change(view, "filter_type", %{"type" => "media"})
assert html =~ "banner.png"
refute html =~ "logo.svg"
end
test "filters by search", %{conn: conn} do
{:ok, _} = Media.upload_image(@svg_attrs)
{:ok, _} = Media.upload_image(@raster_attrs)
{:ok, view, _html} = live(conn, ~p"/admin/media")
html = render_keyup(view, "filter_search", %{"value" => "banner"})
assert html =~ "banner.png"
refute html =~ "logo.svg"
end
test "selects image and shows detail panel", %{conn: conn} do
{:ok, image} = Media.upload_image(@raster_attrs)
{:ok, view, _html} = live(conn, ~p"/admin/media")
html = render_click(view, "select_image", %{"id" => image.id})
assert html =~ "Image details"
assert html =~ "banner.png"
assert html =~ "Alt text"
end
test "updates metadata", %{conn: conn} do
{:ok, image} = Media.upload_image(@raster_attrs)
{:ok, view, _html} = live(conn, ~p"/admin/media")
render_click(view, "select_image", %{"id" => image.id})
render_submit(view, "update_metadata", %{
"metadata" => %{"alt" => "Updated alt", "caption" => "New caption", "tags" => "hero"}
})
updated = Media.get_image(image.id)
assert updated.alt == "Updated alt"
assert updated.caption == "New caption"
assert updated.tags == "hero"
end
test "deletes orphaned image", %{conn: conn} do
{:ok, image} = Media.upload_image(@raster_attrs)
{:ok, view, _html} = live(conn, ~p"/admin/media")
render_click(view, "select_image", %{"id" => image.id})
render_click(view, "confirm_delete")
html = render_click(view, "delete_image")
assert html =~ "Image deleted"
assert Media.get_image(image.id) == nil
end
test "refuses to delete in-use image", %{conn: conn} do
{:ok, image} = Media.upload_image(@svg_attrs)
Berrypod.Settings.update_theme_settings(%{logo_image_id: image.id})
{:ok, view, _html} = live(conn, ~p"/admin/media")
render_click(view, "select_image", %{"id" => image.id})
render_click(view, "confirm_delete")
html = render_click(view, "delete_image")
assert html =~ "Cannot delete"
assert Media.get_image(image.id) != nil
end
test "orphan filter shows only unreferenced images", %{conn: conn} do
{:ok, logo} = Media.upload_image(@svg_attrs)
{:ok, _orphan} = Media.upload_image(@raster_attrs)
Berrypod.Settings.update_theme_settings(%{logo_image_id: logo.id})
{:ok, view, _html} = live(conn, ~p"/admin/media")
html = render_click(view, "toggle_orphans")
assert html =~ "banner.png"
refute html =~ "logo.svg"
end
test "shows usage info for referenced image", %{conn: conn} do
{:ok, image} = Media.upload_image(@svg_attrs)
Berrypod.Settings.update_theme_settings(%{logo_image_id: image.id})
{:ok, view, _html} = live(conn, ~p"/admin/media")
html = render_click(view, "select_image", %{"id" => image.id})
assert html =~ "Used in"
assert html =~ "Logo"
end
test "shows no-alt-text warning for images without alt", %{conn: conn} do
{:ok, _} = Media.upload_image(Map.delete(@raster_attrs, :alt))
{:ok, _view, html} = live(conn, ~p"/admin/media")
assert html =~ "No alt text"
end
end
end