diff options
| author | erdgeist <erdgeist@erdgeist.org> | 2026-07-20 19:45:43 +0200 |
|---|---|---|
| committer | erdgeist <erdgeist@erdgeist.org> | 2026-07-20 19:45:43 +0200 |
| commit | 836308471f8d31ccdcdd3a5bd88bc76cc1c0831b (patch) | |
| tree | 821a526d4d10870654a38d07a4eb53a2180ba313 /test/models | |
| parent | d89e2de48a5e16ff01ec627e79c76b833e4618cf (diff) | |
Make headline images explicit, add asset credits
- related_assets gains a `headline` boolean (DB-enforced: at most one
per page), replacing "first image by position" as the headline
rule. A rake task backfills the current first image on every live
head/draft, so nothing changes visually until an editor changes it.
- The image picker sidebar gets a star toggle reflecting the flag;
the TinyMCE inline-image picker's badge now reads it too, instead
of assuming position 0.
- No headline chosen (or none attached) now falls back to the
gallery-count caption itself becoming the lightbox trigger, instead
of the gallery being unreachable.
- Assets gain creator, source_url, and license_key (against a new
config/asset_licenses.yml dictionary). asset_credit renders a
degrading attribution line, reused as a hidden per-image glightbox
caption so credit is one click away for every image, not only the
headline's always-visible one.
- Fixed: asset thumbnails rendered unconditionally regardless of
whether a real variant exists on disk. Asset#has_variant? checks
file existence, not content type -- some legacy PDFs have real
pre-rewrite thumbnails a content-type check would have hidden.
- assets#new/edit rebuilt onto the same node_description/node_content
layout as assets#show, picking up the three new fields in the
process.
Diffstat (limited to 'test/models')
| -rw-r--r-- | test/models/asset_license_test.rb | 31 | ||||
| -rw-r--r-- | test/models/asset_test.rb | 23 | ||||
| -rw-r--r-- | test/models/helpers/content_helper_test.rb | 78 | ||||
| -rw-r--r-- | test/models/related_asset_test.rb | 44 |
4 files changed, 169 insertions, 7 deletions
diff --git a/test/models/asset_license_test.rb b/test/models/asset_license_test.rb new file mode 100644 index 0000000..9f2d04f --- /dev/null +++ b/test/models/asset_license_test.rb | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | require 'test_helper' | ||
| 2 | |||
| 3 | class AssetLicenseTest < ActiveSupport::TestCase | ||
| 4 | test "find returns a populated entry for a known key" do | ||
| 5 | entry = AssetLicense.find("cc_by_4") | ||
| 6 | |||
| 7 | assert_equal "cc_by_4", entry.key | ||
| 8 | assert_equal "https://creativecommons.org/licenses/by/4.0/", entry.url | ||
| 9 | assert entry.requires_attribution | ||
| 10 | assert_equal "license", entry.style | ||
| 11 | end | ||
| 12 | |||
| 13 | test "find returns nil for an unknown or blank key" do | ||
| 14 | assert_nil AssetLicense.find("not_a_real_license") | ||
| 15 | assert_nil AssetLicense.find(nil) | ||
| 16 | assert_nil AssetLicense.find("") | ||
| 17 | end | ||
| 18 | |||
| 19 | test "keys includes every dictionary entry" do | ||
| 20 | assert_includes AssetLicense.keys, "cc0" | ||
| 21 | assert_includes AssetLicense.keys, "own_work" | ||
| 22 | end | ||
| 23 | |||
| 24 | test "a note-style entry has no attribution requirement and no url" do | ||
| 25 | entry = AssetLicense.find("own_work") | ||
| 26 | |||
| 27 | assert_not entry.requires_attribution | ||
| 28 | assert_nil entry.url | ||
| 29 | assert_equal "note", entry.style | ||
| 30 | end | ||
| 31 | end | ||
diff --git a/test/models/asset_test.rb b/test/models/asset_test.rb index a1041e4..d246abe 100644 --- a/test/models/asset_test.rb +++ b/test/models/asset_test.rb | |||
| @@ -19,5 +19,26 @@ class AssetTest < ActiveSupport::TestCase | |||
| 19 | assert_equal 0, draft.assets.length | 19 | assert_equal 0, draft.assets.length |
| 20 | assert_equal 0, RelatedAsset.count | 20 | assert_equal 0, RelatedAsset.count |
| 21 | end | 21 | end |
| 22 | 22 | ||
| 23 | test "image? is true for supported image content types" do | ||
| 24 | assert Asset.new(:upload_content_type => "image/png").image? | ||
| 25 | assert Asset.new(:upload_content_type => "image/jpeg").image? | ||
| 26 | end | ||
| 27 | |||
| 28 | test "image? is false for non-image content types" do | ||
| 29 | assert_not Asset.new(:upload_content_type => "application/pdf").image? | ||
| 30 | assert_not Asset.new(:upload_content_type => nil).image? | ||
| 31 | end | ||
| 32 | |||
| 33 | test "license_key must be a known dictionary key" do | ||
| 34 | asset = Asset.new(:license_key => "not_a_real_license") | ||
| 35 | I18n.with_locale(:en) do | ||
| 36 | assert_not asset.valid? | ||
| 37 | assert_includes asset.errors[:license_key], "is not included in the list" | ||
| 38 | end | ||
| 39 | end | ||
| 40 | |||
| 41 | test "license_key may be blank" do | ||
| 42 | assert Asset.new(:license_key => nil).valid? | ||
| 43 | end | ||
| 23 | end | 44 | end |
diff --git a/test/models/helpers/content_helper_test.rb b/test/models/helpers/content_helper_test.rb index a7ed478..d6c7b43 100644 --- a/test/models/helpers/content_helper_test.rb +++ b/test/models/helpers/content_helper_test.rb | |||
| @@ -2,7 +2,81 @@ require 'test_helper' | |||
| 2 | 2 | ||
| 3 | class ContentHelperTest < ActionView::TestCase | 3 | class ContentHelperTest < ActionView::TestCase |
| 4 | test "weekday_abbr delegates through the current I18n locale" do | 4 | test "weekday_abbr delegates through the current I18n locale" do |
| 5 | I18n.locale = :de | 5 | I18n.with_locale(:de) do |
| 6 | assert_equal "Mo", weekday_abbr(Time.parse("2026-07-06")) | 6 | assert_equal "Mo", weekday_abbr(Time.parse("2026-07-06")) |
| 7 | end | ||
| 8 | end | ||
| 9 | |||
| 10 | test "asset_credit returns nil for a nil asset" do | ||
| 11 | assert_nil asset_credit(nil) | ||
| 12 | end | ||
| 13 | |||
| 14 | test "asset_credit returns nil when creator, source, and license are all blank" do | ||
| 15 | assert_nil asset_credit(Asset.new(:name => "blank")) | ||
| 16 | end | ||
| 17 | |||
| 18 | test "asset_credit renders creator, source link, and license link when all are present" do | ||
| 19 | asset = Asset.new(:name => "demo", :creator => "Jane Doe", | ||
| 20 | :source_url => "https://example.org/photo", :license_key => "cc_by_4") | ||
| 21 | |||
| 22 | result = I18n.with_locale(:en) { asset_credit(asset) } | ||
| 23 | |||
| 24 | assert_match %r{<a href="https://example.org/photo">Photo demo</a>}, result | ||
| 25 | assert_match "by Jane Doe", result | ||
| 26 | assert_match %r{<a href="https://creativecommons.org/licenses/by/4.0/">Licensed under CC BY 4.0</a>}, result | ||
| 27 | end | ||
| 28 | |||
| 29 | test "asset_credit falls back to plain text when source_url is blank" do | ||
| 30 | asset = Asset.new(:name => "demo", :creator => "Jane Doe", :license_key => "cc_by_4") | ||
| 31 | assert_no_match %r{<a href="[^"]*">Photo demo</a>}, I18n.with_locale(:en) { asset_credit(asset) } | ||
| 32 | end | ||
| 33 | |||
| 34 | test "asset_credit omits the 'by' clause when creator is blank" do | ||
| 35 | asset = Asset.new(:name => "demo", :source_url => "https://example.org/photo", :license_key => "cc_by_4") | ||
| 36 | assert_no_match "by ", I18n.with_locale(:en) { asset_credit(asset) } | ||
| 37 | end | ||
| 38 | |||
| 39 | test "a note-style license renders with no 'Licensed under' prefix and no link" do | ||
| 40 | asset = Asset.new(:name => "demo", :creator => "CCC", :license_key => "own_work") | ||
| 41 | result = I18n.with_locale(:en) { asset_credit(asset) } | ||
| 42 | |||
| 43 | assert_match "Own work", result | ||
| 44 | assert_no_match "Licensed under", result | ||
| 45 | assert_no_match "<a", result | ||
| 46 | end | ||
| 47 | |||
| 48 | test "asset_credit degrades gracefully when the license_key is no longer in the dictionary" do | ||
| 49 | asset = Asset.new(:name => "demo", :creator => "Jane Doe") | ||
| 50 | asset.license_key = "a_retired_key" | ||
| 51 | |||
| 52 | result = I18n.with_locale(:en) { asset_credit(asset) } | ||
| 53 | |||
| 54 | assert_match "Photo demo by Jane Doe", result | ||
| 55 | assert_no_match "Licensed under", result | ||
| 56 | end | ||
| 57 | |||
| 58 | test "headline_image renders nothing when no images are attached" do | ||
| 59 | node = Node.root.children.create!(:slug => "headline_image_empty_test") | ||
| 60 | @page = node.draft | ||
| 61 | assert_nil headline_image | ||
| 62 | end | ||
| 63 | |||
| 64 | test "headline_image renders the flagged headline asset" do | ||
| 65 | node = Node.root.children.create!(:slug => "headline_image_flagged_test") | ||
| 66 | asset = Asset.create!(:name => "flagged", :upload_content_type => "image/png") | ||
| 67 | node.draft.assets << asset | ||
| 68 | node.draft.related_assets.find_by(:asset_id => asset.id).update!(:headline => true) | ||
| 69 | @page = node.draft | ||
| 70 | |||
| 71 | assert_match "glightbox", headline_image | ||
| 72 | end | ||
| 73 | |||
| 74 | test "headline_image falls back to a gallery-open caption when no headline is flagged" do | ||
| 75 | node = Node.root.children.create!(:slug => "headline_image_no_flag_test") | ||
| 76 | asset = Asset.create!(:name => "unflagged", :upload_content_type => "image/png") | ||
| 77 | node.draft.assets << asset | ||
| 78 | @page = node.draft | ||
| 79 | |||
| 80 | I18n.with_locale(:en) { assert_match t(:open_gallery), headline_image } | ||
| 7 | end | 81 | end |
| 8 | end | 82 | end |
diff --git a/test/models/related_asset_test.rb b/test/models/related_asset_test.rb index a739e6b..710b4cc 100644 --- a/test/models/related_asset_test.rb +++ b/test/models/related_asset_test.rb | |||
| @@ -1,8 +1,44 @@ | |||
| 1 | require 'test_helper' | 1 | require 'test_helper' |
| 2 | 2 | ||
| 3 | class RelatedImageTest < ActiveSupport::TestCase | 3 | class RelatedAssetTest < ActiveSupport::TestCase |
| 4 | # Replace this with your real tests. | 4 | test "headline can be set on an image asset" do |
| 5 | test "the truth" do | 5 | node = Node.root.children.create!(:slug => "related_asset_headline_image_test") |
| 6 | assert true | 6 | asset = Asset.create!(:name => "photo", :upload_content_type => "image/png") |
| 7 | node.draft.assets << asset | ||
| 8 | related = node.draft.related_assets.find_by(:asset_id => asset.id) | ||
| 9 | |||
| 10 | related.headline = true | ||
| 11 | assert related.valid? | ||
| 12 | end | ||
| 13 | |||
| 14 | test "headline cannot be set on a non-image asset" do | ||
| 15 | node = Node.root.children.create!(:slug => "related_asset_headline_pdf_test") | ||
| 16 | asset = Asset.create!(:name => "programme", :upload_content_type => "application/pdf") | ||
| 17 | node.draft.assets << asset | ||
| 18 | related = node.draft.related_assets.find_by(:asset_id => asset.id) | ||
| 19 | |||
| 20 | related.headline = true | ||
| 21 | assert_not related.valid? | ||
| 22 | assert_includes related.errors[:headline], "can only be set on image assets" | ||
| 23 | end | ||
| 24 | |||
| 25 | test "the headline validation does not raise when asset is missing" do | ||
| 26 | related = RelatedAsset.new(:headline => true) | ||
| 27 | assert_not related.valid? | ||
| 28 | end | ||
| 29 | |||
| 30 | test "at most one headline per page is enforced at the database level" do | ||
| 31 | node = Node.root.children.create!(:slug => "related_asset_headline_uniqueness_test") | ||
| 32 | first = Asset.create!(:name => "first", :upload_content_type => "image/png") | ||
| 33 | second = Asset.create!(:name => "second", :upload_content_type => "image/png") | ||
| 34 | node.draft.assets << first | ||
| 35 | node.draft.assets << second | ||
| 36 | |||
| 37 | node.draft.related_assets.find_by(:asset_id => first.id).update!(:headline => true) | ||
| 38 | second_related = node.draft.related_assets.find_by(:asset_id => second.id) | ||
| 39 | |||
| 40 | assert_raises(ActiveRecord::RecordNotUnique) do | ||
| 41 | RelatedAsset.where(:id => second_related.id).update_all(:headline => true) | ||
| 42 | end | ||
| 7 | end | 43 | end |
| 8 | end | 44 | end |
