image view: implement gallery-based layout, metadata dropdown, and thumbnails; add styles for image shadow; update tests

This commit is contained in:
2026-03-23 20:25:40 -04:00
parent 5122ece42b
commit a97fb6078b
5 changed files with 468 additions and 74 deletions

View File

@@ -299,6 +299,68 @@ class GalleryViewTests(GalleryBaseTests):
self.assertEqual(response.context["image_path"].name, "beta.jpg")
self.assertEqual(response.context["prev"], "alpha.jpg")
self.assertEqual(response.context["next"], "gamma.jpg")
# New context keys for image view should be present
self.assertIn("prev_url", response.context)
self.assertIn("next_url", response.context)
self.assertIsNone(response.context.get("prev_url")) if response.context.get(
"prev"
) is None else self.assertTrue(
"/gallery/alpha.jpg" in response.context.get("prev_url")
)
self.assertIsNone(response.context.get("next_url")) if response.context.get(
"next"
) is None else self.assertTrue(
"/gallery/gamma.jpg" in response.context.get("next_url")
)
# Thumbnails (if available) should be provided as /thumbs/... or be None
prev_thumb = response.context.get("prev_thumb")
next_thumb = response.context.get("next_thumb")
if prev_thumb is not None:
self.assertTrue(prev_thumb.startswith("/thumbs/"))
if next_thumb is not None:
self.assertTrue(next_thumb.startswith("/thumbs/"))
# Breadcrumbs should include filename as last label and it must be non-clickable (path None)
breadcrumbs = response.context.get("breadcrumbs")
self.assertIsNotNone(breadcrumbs)
self.assertEqual(breadcrumbs[-1]["label"], "beta.jpg")
self.assertIsNone(breadcrumbs[-1]["path"])
# Back and Home URLs should exist and preserve query state
back_url = response.context.get("back_url")
home_url = response.context.get("home_url")
self.assertIsNotNone(back_url)
self.assertIsNotNone(home_url)
# For images in root both should be the gallery root with query params
# normalize any './' segments to compare reliably
norm = lambda u: u.replace("/./", "/") if u is not None else u
self.assertEqual(norm(back_url), norm(home_url))
self.assertIn("/gallery/", norm(back_url))
self.assertIn("sort=abc", back_url)
self.assertIn("theme=dark", back_url)
# Back and Home thumbnails should be available and point to /thumbs/
back_thumb = response.context.get("back_thumb")
home_thumb = response.context.get("home_thumb")
self.assertIsNotNone(back_thumb)
self.assertIsNotNone(home_thumb)
self.assertTrue(back_thumb.startswith("/thumbs/"))
self.assertTrue(home_thumb.startswith("/thumbs/"))
self.assertEqual(back_thumb, home_thumb)
# Image metadata should be present and sensible
image_meta = response.context.get("image_meta")
self.assertIsNotNone(image_meta)
self.assertEqual(image_meta.get("filename"), "beta.jpg")
self.assertEqual(image_meta.get("width"), 300)
self.assertEqual(image_meta.get("height"), 200)
self.assertIsNotNone(image_meta.get("filesize"))
self.assertTrue(
"KB" in image_meta.get("filesize") or "MB" in image_meta.get("filesize")
)
self.assertIsNotNone(image_meta.get("created"))
self.assertIsNotNone(image_meta.get("modified"))
def test_sort_modes_recent_and_tnecer_use_mtime(self):
recent = self.client.get("/gallery/?sort=recent")
@@ -365,9 +427,11 @@ class GalleryTemplateTests(GalleryBaseTests):
response = self.client.get("/gallery/?theme=dark")
self.assertEqual(response.status_code, 200)
body = response.content.decode("utf-8")
self.assertIn(".search-input::placeholder", body)
self.assertIn("color: var(--muted);", body)
# Styles are served via static files; assert the rules exist in the stylesheet
css_path = Path(__file__).resolve().parent / "static" / "css" / "styles.css"
css = css_path.read_text(encoding="utf-8")
self.assertIn(".search-input::placeholder", css)
self.assertIn("color: var(--muted);", css)
def test_sort_dropdown_active_option_underlined(self):
response = self.client.get("/gallery/?sort=recent")
@@ -381,5 +445,7 @@ class GalleryTemplateTests(GalleryBaseTests):
response = self.client.get("/gallery/")
self.assertEqual(response.status_code, 200)
body = response.content.decode("utf-8")
self.assertIn("box-shadow: 0 5px 20px", body)
# The requested drop shadows should be defined in the stylesheet
css_path = Path(__file__).resolve().parent / "static" / "css" / "styles.css"
css = css_path.read_text(encoding="utf-8")
self.assertIn("box-shadow: 0 5px 20px", css)