Improve credit line in image list (#6295)
Some checks are pending
Android CI / Run tests and generate APK (push) Waiting to run

- When author is not uploader, show both.
- When failing to parse author from HTML, use structured data.
This commit is contained in:
Yusuke Matsubara 2025-04-23 23:23:09 +09:00 committed by GitHub
parent 30762971db
commit 329a68216e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 363 additions and 81 deletions

View file

@ -50,6 +50,7 @@ fun media(
licenseUrl: String? = "licenseUrl",
author: String? = "creator",
user: String? = "user",
creatorName: String? = null,
pageId: String = "pageId",
categories: List<String>? = listOf("categories"),
coordinates: LatLng? = LatLng(0.0, 0.0, 0.0f),
@ -67,6 +68,7 @@ fun media(
licenseUrl,
author,
user,
creatorName,
categories,
coordinates,
captions,

View file

@ -8,6 +8,7 @@ import androidx.test.core.app.ApplicationProvider
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.soloader.SoLoader
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.MediaDataExtractor
import fr.free.nrw.commons.R
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.TestUtility.setFinalStatic
@ -46,6 +47,9 @@ class ContributionViewHolderUnitTests {
@Mock
private lateinit var mediaClient: MediaClient
@Mock
private lateinit var mediaDataExtractor: MediaDataExtractor
@Mock
private lateinit var uri: Uri
@ -66,8 +70,9 @@ class ContributionViewHolderUnitTests {
SoLoader.setInTestMode()
Fresco.initialize(ApplicationProvider.getApplicationContext())
activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get()
compositeDisposable = CompositeDisposable()
parent = LayoutInflater.from(activity).inflate(R.layout.layout_contribution, null)
contributionViewHolder = ContributionViewHolder(parent, callback, mediaClient)
contributionViewHolder = ContributionViewHolder(parent, callback, compositeDisposable, mediaClient, mediaDataExtractor)
bindind = LayoutContributionBinding.bind(parent)

View file

@ -10,6 +10,7 @@ import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import java.lang.IllegalArgumentException
@ -42,23 +43,61 @@ class MediaConverterTest {
@Test
fun testConvertIfThumbUrlBlank() {
Mockito.`when`(imageInfo.getMetadata()).thenReturn(metadata)
Mockito.`when`(imageInfo.getThumbUrl()).thenReturn("")
Mockito.`when`(imageInfo.getOriginalUrl()).thenReturn("originalUrl")
Mockito.`when`(imageInfo.getMetadata()?.licenseUrl()).thenReturn("licenseUrl")
Mockito.`when`(imageInfo.getMetadata()?.dateTime()).thenReturn("yyyy-MM-dd HH:mm:ss")
`when`(imageInfo.getMetadata()).thenReturn(metadata)
`when`(imageInfo.getThumbUrl()).thenReturn("")
`when`(imageInfo.getOriginalUrl()).thenReturn("originalUrl")
`when`(metadata.licenseUrl()).thenReturn("licenseUrl")
`when`(metadata.dateTime()).thenReturn("yyyy-MM-dd HH:mm:ss")
`when`(metadata.artist()).thenReturn("Foo Bar")
media = mediaConverter.convert(page, entity, imageInfo)
assertEquals(media.thumbUrl, media.imageUrl, "originalUrl")
}
@Test
fun testConvertIfThumbUrlNotBlank() {
Mockito.`when`(imageInfo.getMetadata()).thenReturn(metadata)
Mockito.`when`(imageInfo.getThumbUrl()).thenReturn("thumbUrl")
Mockito.`when`(imageInfo.getOriginalUrl()).thenReturn("originalUrl")
Mockito.`when`(imageInfo.getMetadata()?.licenseUrl()).thenReturn("licenseUrl")
Mockito.`when`(imageInfo.getMetadata()?.dateTime()).thenReturn("yyyy-MM-dd HH:mm:ss")
`when`(imageInfo.getMetadata()).thenReturn(metadata)
`when`(imageInfo.getThumbUrl()).thenReturn("thumbUrl")
`when`(imageInfo.getOriginalUrl()).thenReturn("originalUrl")
`when`(metadata.licenseUrl()).thenReturn("licenseUrl")
`when`(metadata.dateTime()).thenReturn("yyyy-MM-dd HH:mm:ss")
`when`(metadata.artist()).thenReturn("Foo Bar")
media = mediaConverter.convert(page, entity, imageInfo)
assertEquals(media.thumbUrl, "thumbUrl")
}
@Test
fun `test converting artist value (author) with html links`() {
`when`(imageInfo.getMetadata()).thenReturn(metadata)
`when`(imageInfo.getThumbUrl()).thenReturn("thumbUrl")
`when`(imageInfo.getOriginalUrl()).thenReturn("originalUrl")
`when`(metadata.licenseUrl()).thenReturn("licenseUrl")
`when`(metadata.dateTime()).thenReturn("yyyy-MM-dd HH:mm:ss")
`when`(metadata.artist()).thenReturn("<a href=\"//commons.wikimedia.org/wiki/User:Foo_Bar\" title=\"Foo Bar\">Foo Bar</a>")
// Artist values like above is very common, found in file pages created via UploadWizard
media = mediaConverter.convert(page, entity, imageInfo)
assertEquals("Foo Bar", media.author)
}
@Test
fun `test convert artist value (author) in plain text`() {
`when`(imageInfo.getMetadata()).thenReturn(metadata)
`when`(imageInfo.getThumbUrl()).thenReturn("thumbUrl")
`when`(imageInfo.getOriginalUrl()).thenReturn("originalUrl")
`when`(metadata.licenseUrl()).thenReturn("licenseUrl")
`when`(metadata.dateTime()).thenReturn("yyyy-MM-dd HH:mm:ss")
`when`(metadata.artist()).thenReturn("Foo Bar")
media = mediaConverter.convert(page, entity, imageInfo)
assertEquals("Foo Bar", media.author)
}
@Test
fun `test convert artist value (author) containing red link`() {
`when`(imageInfo.getMetadata()).thenReturn(metadata)
`when`(imageInfo.getThumbUrl()).thenReturn("thumbUrl")
`when`(imageInfo.getOriginalUrl()).thenReturn("originalUrl")
`when`(metadata.licenseUrl()).thenReturn("licenseUrl")
`when`(metadata.dateTime()).thenReturn("yyyy-MM-dd HH:mm:ss")
`when`(metadata.artist()).thenReturn("<a href=\"/w/index.php?title=User:Foo&action=edit&redlink=1\" class=\"new\" title=\"User:Foo (page does not exist)\">Foo</a>")
media = mediaConverter.convert(page, entity, imageInfo)
assertEquals("Foo", media.author)
}
}

View file

@ -0,0 +1,78 @@
package fr.free.nrw.commons.utils
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.media.IdAndLabels
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class, qualifiers="en-rUS")
class MediaAttributionUtilTest {
@Mock
private lateinit var appContext: Context
@Before
fun setup() {
appContext = ApplicationProvider.getApplicationContext()
}
@Test
fun getTagLineWithUploaderOnly() {
val media = mock(Media::class.java)
whenever(media.user).thenReturn("TestUploader")
whenever(media.author).thenReturn(null)
assertEquals("Uploaded by: TestUploader",
MediaAttributionUtil.getTagLine(media, appContext))
}
@Test
fun `get tag line from same author and uploader`() {
val media = mock(Media::class.java)
whenever(media.user).thenReturn("TestUser")
whenever(media.getAttributedAuthor()).thenReturn("TestUser")
assertEquals("Created and uploaded by: TestUser",
MediaAttributionUtil.getTagLine(media, appContext))
}
@Test
fun `get creator name from EN label`() {
assertEquals("FooBar",
MediaAttributionUtil.getCreatorName(listOf(IdAndLabels("Q1", mapOf("en" to "FooBar")))))
}
@Test
fun `get creator name from ES label`() {
assertEquals("FooBar",
MediaAttributionUtil.getCreatorName(listOf(IdAndLabels("Q2", mapOf("es" to "FooBar")))))
}
@Test
fun `get creator name from EN label and ignore ES label`() {
assertEquals("Bar",
MediaAttributionUtil.getCreatorName(listOf(
IdAndLabels("Q3", mapOf("en" to "Bar", "es" to "Foo")))))
}
@Test
fun `get creator name from two creators`() {
val name = MediaAttributionUtil.getCreatorName(listOf(
IdAndLabels("Q1", mapOf("en" to "Foo")),
IdAndLabels("Q1", mapOf("en" to "Bar"))
))
assertNotNull(name)
assertTrue(name!!.contains("Foo"))
assertTrue(name.contains("Bar"))
}
}