Merge pull request #330: bound attachment preview work

Reviewed and merged by GPT on behalf of DHH. Keep both boost-cache and preview-loading regressions.
This commit is contained in:
GPT on behalf of DHH
2026-10-07 10:33:02 +02:00
8 changed files with 198 additions and 5 deletions
+16
View File
@@ -67,6 +67,22 @@ class MessagesCachingTest < ActionDispatch::IntegrationTest
assert_equal in_order, css_select("##{dom_id(messages(:fourth), :boosts)} .boost").map { it["id"] }
end
test "a page of messages loads whether their previews were made along with the messages, not one at a time" do
room = rooms(:watercooler)
2.times do |copy|
{ "moon.jpg" => "image/jpeg", "alpha-centuri.mov" => "video/quicktime" }.each do |file, content_type|
room.messages.create_with_attachment! creator: users(:david), client_message_id: "#{copy}-#{file}", attachment: fixture_file_upload(file, content_type)
end
end
assert_no_queries_match(/"active_storage_\w+"\."(id|blob_id|record_id)" = \?/) do
get room_messages_url(room)
end
assert_response :success
assert_select "img[src*='moon.jpg']", count: 2
assert_select "video[poster]", count: 2
end
private
def with_memory_cache
old_cache = Rails.cache
+48
View File
@@ -24,4 +24,52 @@ class MessagesHelperTest < ActionView::TestCase
assert_match /<a href="https:\/\/example\.com"[^>]*>example<\/a>/, presentation
assert_match /<strong>bold<\/strong>/, presentation
end
test "message_presentation shows an image's thumbnail made when it was posted" do
presentation = view.message_presentation(attachment_message("moon.jpg", "image/jpeg", processed: true))
assert_match %r{<img[^>]+src="[^"]*/representations/[^"]*moon\.jpg"}, presentation
end
test "message_presentation links an image whose thumbnail wasn't made, rather than making it on view" do
presentation = view.message_presentation(attachment_message("moon.jpg", "image/jpeg", processed: false))
assert_no_match %r{/representations/}, presentation
assert_match %r{<span>moon\.jpg</span>}, presentation
end
test "message_presentation gives a video the poster made when it was posted" do
presentation = view.message_presentation(attachment_message("alpha-centuri.mov", "video/quicktime", processed: true))
assert_match %r{<video[^>]+poster="[^"]*/representations/[^"]*alpha-centuri}, presentation
end
test "message_presentation shows a video whose poster wasn't made without one, rather than making it on view" do
presentation = view.message_presentation(attachment_message("alpha-centuri.mov", "video/quicktime", processed: false))
assert_match %r{<video[^>]+src="[^"]*alpha-centuri\.mov"}, presentation
assert_no_match %r{poster=|/representations/}, presentation
end
test "message_presentation shows a video whose frame was drawn but whose poster wasn't made without one" do
message = attachment_message("alpha-centuri.mov", "video/quicktime", processed: false)
message.attachment.preview(format: :jpg).processed
assert message.attachment.preview(:poster).processed?
presentation = view.message_presentation(message.reload)
assert_match %r{<video[^>]+src="[^"]*alpha-centuri\.mov"}, presentation
assert_no_match %r{poster=|/representations/}, presentation
end
private
def attachment_message(file, content_type, processed:)
attributes = { creator: users(:jason), client_message_id: "0015", attachment: fixture_file_upload(file, content_type) }
if processed
rooms(:pets).messages.create_with_attachment!(attributes)
else
rooms(:pets).messages.create!(attributes)
end
end
end
+61
View File
@@ -34,7 +34,68 @@ class Message::AttachmentTest < ActiveSupport::TestCase
assert_not message.attachment.preview(format: :webp).image.attached?
end
test "creating a message makes the video poster that the room shows" do
message = create_attachment_message("alpha-centuri.mov", "video/quicktime")
assert_no_difference -> { ActiveStorage::VariantRecord.count } do
message.reload.attachment.preview(:poster).processed
end
end
test "video previews are drawn by the previewer with a time limit, from a frame within the first 5 seconds" do
assert_includes ActiveStorage.previewers, TimeLimitedVideoPreviewer
assert_not_includes ActiveStorage.previewers, ActiveStorage::Previewer::VideoPreviewer
assert_includes ActiveStorage.video_preview_arguments, "gte(t\\,5)"
end
test "creating a message gives up on a video preview that takes longer than the time limit" do
started = nil
message = nil
with_ffmpeg_sleeping(5.seconds) do
stub_const(TimeLimitedVideoPreviewer, :TIME_LIMIT, 0.2) do
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
message = create_attachment_message("alpha-centuri.mov", "video/quicktime")
end
end
assert_operator Process.clock_gettime(Process::CLOCK_MONOTONIC) - started, :<, 2
assert_not message.reload.attachment.preview(format: :webp).image.attached?
end
test "creating a message makes no thumbnail of an image with more pixels than the limit" do
moon = Vips::Image.new_from_file(file_fixture("moon.jpg").to_s)
stub_const(Message::Attachment, :THUMBNAIL_MAX_PIXELS, moon.width * moon.height) do
assert create_attachment_message("moon.jpg", "image/jpeg").attachment.representation(:thumb).processed?
end
stub_const(Message::Attachment, :THUMBNAIL_MAX_PIXELS, moon.width * moon.height - 1) do
assert_not create_attachment_message("moon.jpg", "image/jpeg").attachment.representation(:thumb).processed?
end
end
test "creating a message makes no preview of a video with more pixels than the limit" do
stub_const(Message::Attachment, :THUMBNAIL_MAX_PIXELS, 1) do
message = create_attachment_message("alpha-centuri.mov", "video/quicktime")
assert_not message.reload.attachment.preview(format: :webp).image.attached?
end
end
private
def with_ffmpeg_sleeping(duration)
Tempfile.create("ffmpeg") do |ffmpeg|
ffmpeg.write "#!/bin/sh\n[ \"$1\" = \"-version\" ] && exit 0\nexec sleep #{duration.to_i}\n"
ffmpeg.close
File.chmod 0o755, ffmpeg.path
previous, ActiveStorage.paths[:ffmpeg] = ActiveStorage.paths[:ffmpeg], ffmpeg.path
yield
ensure
ActiveStorage.paths[:ffmpeg] = previous
end
end
def create_attachment_message(file, content_type)
rooms(:hq).messages.create_with_attachment! \
creator: users(:david),