- {% if att.get('is_image') and att.get('thumbnail_url') %}
-

- {% elif att.get('is_image') %}
-

+ {% if att.get('is_image') %}
+
 else att['thumbnail_url'] }})
{% elif att.get('is_video') %}
{% elif att.get('is_audio') %}
diff --git a/devplacepy/templates/_post_card.html b/devplacepy/templates/_post_card.html
index 009f7239..a419d55c 100644
--- a/devplacepy/templates/_post_card.html
+++ b/devplacepy/templates/_post_card.html
@@ -25,7 +25,8 @@
Project: {{ item.project_link.name }}
{% endif %}
- {% if item.attachments %}
+ {% set attachments = item.get('attachments', []) %}
+ {% if attachments %}
{% include "_attachment_display.html" %}
{% endif %}
diff --git a/tests/conftest.py b/tests/conftest.py
index 30f95638..e2c63464 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -319,6 +319,23 @@ def login_user(page, user):
page.wait_for_url("**/feed", timeout=10000, wait_until="domcontentloaded")
+def create_post_with_files(page, content, files, expected_count=1):
+ from playwright.sync_api import expect
+
+ page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
+ page.locator(".feed-fab").first.wait_for(state="visible", timeout=10000)
+ page.locator(".feed-fab").first.click()
+ page.fill("#post-content", content)
+ page.locator("#create-post-modal dp-upload .dp-upload-input").first.set_input_files(
+ files
+ )
+ expect(
+ page.locator("#create-post-modal dp-upload .dp-upload-count").first
+ ).to_have_text(f"({expected_count})", timeout=15000)
+ page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
+ page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
+
+
def assert_share_copies(page, expected_fragment):
from playwright.sync_api import expect
diff --git a/tests/e2e/feed.py b/tests/e2e/feed.py
index 0b4072f7..a52fb4c0 100644
--- a/tests/e2e/feed.py
+++ b/tests/e2e/feed.py
@@ -1,6 +1,6 @@
# retoor
-from tests.conftest import BASE_URL
+from tests.conftest import BASE_URL, create_post_with_files
import time
import requests
from playwright.sync_api import expect
@@ -702,6 +702,28 @@ def test_feed_politics_topic(alice):
assert politics_link.is_visible()
+def test_feed_card_shows_the_post_image(alice):
+ import io
+ from PIL import Image
+
+ page, _ = alice
+ buf = io.BytesIO()
+ Image.new("RGB", (600, 400), (28, 120, 200)).save(buf, "PNG")
+ create_post_with_files(
+ page,
+ "Feed card image rendering check",
+ [{"name": "card.png", "mimeType": "image/png", "buffer": buf.getvalue()}],
+ )
+ page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
+ card = page.locator(
+ ".post-card:has-text('Feed card image rendering check')"
+ ).first
+ card.wait_for(state="visible", timeout=10000)
+ image = card.locator(".attachment-gallery.single .gallery-thumb").first
+ image.wait_for(state="visible", timeout=10000)
+ assert "_thumb" not in image.get_attribute("src")
+
+
def test_create_post_cancel_modal(alice):
page, _ = alice
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
diff --git a/tests/e2e/post.py b/tests/e2e/post.py
index 814c1b92..822f1f00 100644
--- a/tests/e2e/post.py
+++ b/tests/e2e/post.py
@@ -2,7 +2,7 @@
import re
from playwright.sync_api import expect
-from tests.conftest import BASE_URL, assert_share_copies
+from tests.conftest import BASE_URL, assert_share_copies, create_post_with_files
def create_post(page, topic="random", content="Test post content", title=None):
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
page.locator(".feed-fab").first.wait_for(state="visible", timeout=10000)
@@ -13,6 +13,27 @@ def create_post(page, topic="random", content="Test post content", title=None):
page.fill("#post-title", title)
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
+def _png_bytes(color=(0, 128, 255), size=(600, 400)):
+ import io
+ from PIL import Image
+
+ buf = io.BytesIO()
+ Image.new("RGB", size, color).save(buf, "PNG")
+ return buf.getvalue()
+
+
+def _gif_bytes():
+ import io
+ from PIL import Image
+
+ frames = [Image.new("RGB", (40, 40), c) for c in ((255, 0, 0), (0, 0, 255))]
+ buf = io.BytesIO()
+ frames[0].save(
+ buf, "GIF", save_all=True, append_images=frames[1:], duration=120, loop=0
+ )
+ return buf.getvalue()
+
+
def _profile_stars(page, username):
page.goto(f"{BASE_URL}/profile/{username}", wait_until="domcontentloaded")
value = page.locator(
@@ -375,6 +396,57 @@ def test_emoji_picker_opens(alice):
assert page.locator("emoji-picker").first.is_visible()
+def test_single_image_post_shows_the_original_full_size(alice):
+ page, _ = alice
+ create_post_with_files(
+ page,
+ "Post carrying exactly one image",
+ [{"name": "shot.png", "mimeType": "image/png", "buffer": _png_bytes()}],
+ )
+ gallery = page.locator(".attachment-gallery.single")
+ gallery.wait_for(state="visible", timeout=10000)
+ img = gallery.locator(".gallery-thumb").first
+ src = img.get_attribute("src")
+ assert "_thumb" not in src, f"hero image served the 200px thumbnail: {src}"
+ assert src == img.get_attribute("data-full")
+
+
+def test_multiple_image_post_keeps_thumbnails(alice):
+ page, _ = alice
+ create_post_with_files(
+ page,
+ "Post carrying two images",
+ [
+ {"name": "one.png", "mimeType": "image/png", "buffer": _png_bytes()},
+ {
+ "name": "two.png",
+ "mimeType": "image/png",
+ "buffer": _png_bytes((200, 30, 90)),
+ },
+ ],
+ expected_count=2,
+ )
+ page.locator(".attachment-gallery").first.wait_for(state="visible", timeout=10000)
+ assert page.locator(".attachment-gallery.single").count() == 0
+ thumbs = page.locator(".attachment-gallery .gallery-thumb")
+ assert thumbs.count() == 2
+ for i in range(thumbs.count()):
+ assert "_thumb" in thumbs.nth(i).get_attribute("src")
+
+
+def test_animated_gif_post_serves_the_original_file(alice):
+ page, _ = alice
+ create_post_with_files(
+ page,
+ "Post carrying an animated gif",
+ [{"name": "loop.gif", "mimeType": "image/gif", "buffer": _gif_bytes()}],
+ )
+ img = page.locator(".attachment-gallery .gallery-thumb").first
+ img.wait_for(state="visible", timeout=10000)
+ src = img.get_attribute("src")
+ assert src.endswith(".gif"), f"animation lost, served {src}"
+
+
def test_attachment_upload_ui(alice):
import io
from PIL import Image