Deletion Language is in english (#4841)

* Deletion Language is in english

* Added Tests

* Added Tests

* Added Tests
This commit is contained in:
Devarsh Mavani 2022-02-22 08:15:35 +05:30 committed by GitHub
parent 0f515823e9
commit d81587b85a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 144 additions and 18 deletions

View file

@ -4,6 +4,7 @@ import static fr.free.nrw.commons.notification.NotificationHelper.NOTIFICATION_D
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import static fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
@ -78,7 +79,7 @@ public class DeleteHelper {
String fileDeleteString = "{{delete|reason=" + reason + String fileDeleteString = "{{delete|reason=" + reason +
"|subpage=" + media.getFilename() + "|subpage=" + media.getFilename() +
"|day=" + calendar.get(Calendar.DAY_OF_MONTH) + "|day=" + calendar.get(Calendar.DAY_OF_MONTH) +
"|month=" + calendar.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()) + "|month=" + calendar.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.ENGLISH) +
"|year=" + calendar.get(Calendar.YEAR) + "|year=" + calendar.get(Calendar.YEAR) +
"}}"; "}}";
@ -156,16 +157,23 @@ public class DeleteHelper {
ArrayList<Integer> mUserReason = new ArrayList<>(); ArrayList<Integer> mUserReason = new ArrayList<>();
String[] reasonList = {"Reason 1", "Reason 2", "Reason 3"}; String[] reasonList = {"Reason 1", "Reason 2", "Reason 3"};
// Messages posted on-wiki should not be in the app user's locale, but rather in Commons' lingua franca English.
String[] reasonListEnglish = {"Eng1", "Eng2", "Eng3"};
if (problem == ReviewController.DeleteReason.SPAM) { if (problem == ReviewController.DeleteReason.SPAM) {
reasonList[0] = context.getString(R.string.delete_helper_ask_spam_selfie); reasonList[0] = context.getString(R.string.delete_helper_ask_spam_selfie);
reasonList[1] = context.getString(R.string.delete_helper_ask_spam_blurry); reasonList[1] = context.getString(R.string.delete_helper_ask_spam_blurry);
reasonList[2] = context.getString(R.string.delete_helper_ask_spam_nonsense); reasonList[2] = context.getString(R.string.delete_helper_ask_spam_nonsense);
reasonListEnglish[0] = getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_spam_selfie);
reasonListEnglish[1] = getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_spam_blurry);
reasonListEnglish[2] = getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_spam_nonsense);
} else if (problem == ReviewController.DeleteReason.COPYRIGHT_VIOLATION) { } else if (problem == ReviewController.DeleteReason.COPYRIGHT_VIOLATION) {
reasonList[0] = context.getString(R.string.delete_helper_ask_reason_copyright_press_photo); reasonList[0] = context.getString(R.string.delete_helper_ask_reason_copyright_press_photo);
reasonList[1] = context.getString(R.string.delete_helper_ask_reason_copyright_internet_photo); reasonList[1] = context.getString(R.string.delete_helper_ask_reason_copyright_internet_photo);
reasonList[2] = context.getString(R.string.delete_helper_ask_reason_copyright_logo); reasonList[2] = context.getString(R.string.delete_helper_ask_reason_copyright_logo);
reasonListEnglish[0] = getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_reason_copyright_press_photo);
reasonListEnglish[1] = getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_reason_copyright_internet_photo);
reasonListEnglish[2] = getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_reason_copyright_logo);
} }
alert.setMultiChoiceItems(reasonList, checkedItems, (dialogInterface, position, isChecked) -> { alert.setMultiChoiceItems(reasonList, checkedItems, (dialogInterface, position, isChecked) -> {
@ -178,9 +186,10 @@ public class DeleteHelper {
alert.setPositiveButton(context.getString(R.string.ok), (dialogInterface, i) -> { alert.setPositiveButton(context.getString(R.string.ok), (dialogInterface, i) -> {
String reason = context.getString(R.string.delete_helper_ask_alert_set_positive_button_reason) + " "; String reason = getLocalizedResources(context, Locale.ENGLISH).getString(R.string.delete_helper_ask_alert_set_positive_button_reason) + " ";
for (int j = 0; j < mUserReason.size(); j++) { for (int j = 0; j < mUserReason.size(); j++) {
reason = reason + reasonList[mUserReason.get(j)]; reason = reason + reasonListEnglish[mUserReason.get(j)];
if (j != mUserReason.size() - 1) { if (j != mUserReason.size() - 1) {
reason = reason + ", "; reason = reason + ", ";
} }

View file

@ -10,8 +10,10 @@ import static fr.free.nrw.commons.description.EditDescriptionConstants.LIST_OF_D
import static fr.free.nrw.commons.description.EditDescriptionConstants.UPDATED_WIKITEXT; import static fr.free.nrw.commons.description.EditDescriptionConstants.UPDATED_WIKITEXT;
import static fr.free.nrw.commons.description.EditDescriptionConstants.WIKITEXT; import static fr.free.nrw.commons.description.EditDescriptionConstants.WIKITEXT;
import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_LOCATION; import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_LOCATION;
import android.content.res.Resources;
import static fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import java.lang.reflect.Field;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -264,6 +266,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
//Had to make this class variable, to implement various onClicks, which access the media, also I fell why make separate variables when one can serve the purpose //Had to make this class variable, to implement various onClicks, which access the media, also I fell why make separate variables when one can serve the purpose
private Media media; private Media media;
private ArrayList<String> reasonList; private ArrayList<String> reasonList;
private ArrayList<String> reasonListEnglishMappings;
/** /**
* Height stores the height of the frame layout as soon as it is initialised and updates itself on * Height stores the height of the frame layout as soon as it is initialised and updates itself on
@ -324,6 +327,14 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
reasonList.add(getString(R.string.deletion_reason_no_longer_want_public)); reasonList.add(getString(R.string.deletion_reason_no_longer_want_public));
reasonList.add(getString(R.string.deletion_reason_bad_for_my_privacy)); reasonList.add(getString(R.string.deletion_reason_bad_for_my_privacy));
// Add corresponding mappings in english locale so that we can upload it in deletion request
reasonListEnglishMappings = new ArrayList<>();
reasonListEnglishMappings.add(getLocalizedResources(getContext(), Locale.ENGLISH).getString(R.string.deletion_reason_uploaded_by_mistake));
reasonListEnglishMappings.add(getLocalizedResources(getContext(), Locale.ENGLISH).getString(R.string.deletion_reason_publicly_visible));
reasonListEnglishMappings.add(getLocalizedResources(getContext(), Locale.ENGLISH).getString(R.string.deletion_reason_not_interesting));
reasonListEnglishMappings.add(getLocalizedResources(getContext(), Locale.ENGLISH).getString(R.string.deletion_reason_no_longer_want_public));
reasonListEnglishMappings.add(getLocalizedResources(getContext(), Locale.ENGLISH).getString(R.string.deletion_reason_bad_for_my_privacy));
final View view = inflater.inflate(R.layout.fragment_media_detail, container, false); final View view = inflater.inflate(R.layout.fragment_media_detail, container, false);
ButterKnife.bind(this,view); ButterKnife.bind(this,view);
@ -1201,9 +1212,10 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
private void onDeleteClicked(Spinner spinner) { private void onDeleteClicked(Spinner spinner) {
applicationKvStore.putBoolean(String.format(NOMINATING_FOR_DELETION_MEDIA, media.getImageUrl()), true); applicationKvStore.putBoolean(String.format(NOMINATING_FOR_DELETION_MEDIA, media.getImageUrl()), true);
enableProgressBar(); enableProgressBar();
String reason = spinner.getSelectedItem().toString(); String reason = reasonListEnglishMappings.get(spinner.getSelectedItemPosition());
String finalReason = reason;
Single<Boolean> resultSingle = reasonBuilder.getReason(media, reason) Single<Boolean> resultSingle = reasonBuilder.getReason(media, reason)
.flatMap(reasonString -> deleteHelper.makeDeletion(getContext(), media, reason)); .flatMap(reasonString -> deleteHelper.makeDeletion(getContext(), media, finalReason));
resultSingle resultSingle
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View file

@ -1,4 +1,8 @@
package fr.free.nrw.commons.utils; package fr.free.nrw.commons.utils;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import java.util.Locale;
/** /**
* Utilities class for miscellaneous strings * Utilities class for miscellaneous strings
@ -20,4 +24,16 @@ public class LangCodeUtils {
return code; return code;
} }
} }
/**
* Returns configuration for locale of
* our choice regardless of user's device settings
*/
public static Resources getLocalizedResources(Context context, Locale desiredLocale) {
Configuration conf = context.getResources().getConfiguration();
conf = new Configuration(conf);
conf.setLocale(desiredLocale);
Context localizedContext = context.createConfigurationContext(conf);
return localizedContext.getResources();
}
} }

View file

@ -5,22 +5,40 @@ import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.FakeContextWrapper
import fr.free.nrw.commons.Media import fr.free.nrw.commons.Media
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.actions.PageEditClient import fr.free.nrw.commons.actions.PageEditClient
import fr.free.nrw.commons.contributions.ContributionsListFragment
import fr.free.nrw.commons.review.ReviewController
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runner.Runner
import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode
/** /**
* Tests for delete helper * Tests for delete helper
*/ */
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class)
@LooperMode(LooperMode.Mode.PAUSED)
class DeleteHelperTest { class DeleteHelperTest {
@Mock
private lateinit var callback: ReviewController.ReviewCallback
@Mock @Mock
internal lateinit var pageEditClient: PageEditClient internal lateinit var pageEditClient: PageEditClient
@ -58,7 +76,6 @@ class DeleteHelperTest {
val creatorName = "Creator" val creatorName = "Creator"
whenever(media.author).thenReturn("$creatorName") whenever(media.author).thenReturn("$creatorName")
whenever(media.filename).thenReturn("Test file.jpg") whenever(media.filename).thenReturn("Test file.jpg")
val makeDeletion = deleteHelper.makeDeletion(context, media, "Test reason")?.blockingGet() val makeDeletion = deleteHelper.makeDeletion(context, media, "Test reason")?.blockingGet()
assertNotNull(makeDeletion) assertNotNull(makeDeletion)
assertTrue(makeDeletion!!) assertTrue(makeDeletion!!)
@ -113,6 +130,18 @@ class DeleteHelperTest {
deleteHelper.makeDeletion(context, media, "Test reason")?.blockingGet() deleteHelper.makeDeletion(context, media, "Test reason")?.blockingGet()
} }
@Test
fun askReasonAndExecuteSpamTest() {
val mContext = RuntimeEnvironment.getApplication().applicationContext
deleteHelper.askReasonAndExecute(media, mContext, "My Question", ReviewController.DeleteReason.SPAM, callback)
}
@Test
fun askReasonAndExecuteCopyrightViolationTest() {
val mContext = RuntimeEnvironment.getApplication().applicationContext
deleteHelper.askReasonAndExecute(media, mContext, "My Question", ReviewController.DeleteReason.COPYRIGHT_VIOLATION, callback);
}
@Test(expected = RuntimeException::class) @Test(expected = RuntimeException::class)
fun makeDeletionForEmptyCreatorName() { fun makeDeletionForEmptyCreatorName() {
whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))

View file

@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.os.Looper
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
@ -18,12 +19,9 @@ import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.generic.GenericDraweeHierarchy import com.facebook.drawee.generic.GenericDraweeHierarchy
import com.facebook.drawee.view.SimpleDraweeView import com.facebook.drawee.view.SimpleDraweeView
import com.facebook.soloader.SoLoader import com.facebook.soloader.SoLoader
import fr.free.nrw.commons.*
import fr.free.nrw.commons.LocationPicker.LocationPickerActivity import fr.free.nrw.commons.LocationPicker.LocationPickerActivity
import fr.free.nrw.commons.Media
import org.robolectric.Shadows.shadowOf import org.robolectric.Shadows.shadowOf
import fr.free.nrw.commons.R
import fr.free.nrw.commons.TestAppAdapter
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.category.CategoryEditSearchRecyclerViewAdapter import fr.free.nrw.commons.category.CategoryEditSearchRecyclerViewAdapter
import fr.free.nrw.commons.explore.SearchActivity import fr.free.nrw.commons.explore.SearchActivity
import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.kvstore.JsonKvStore
@ -32,17 +30,24 @@ import fr.free.nrw.commons.location.LocationServiceManager
import fr.free.nrw.commons.ui.widget.HtmlTextView import fr.free.nrw.commons.ui.widget.HtmlTextView
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.R
import fr.free.nrw.commons.TestAppAdapter
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.contributions.ContributionViewHolder
import fr.free.nrw.commons.delete.DeleteHelper
import fr.free.nrw.commons.delete.ReasonBuilder
import fr.free.nrw.commons.utils.ImageUtils
import io.reactivex.Single
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.Mock import org.mockito.*
import org.mockito.Mockito import org.mockito.Mockito.*
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import org.powermock.reflect.Whitebox import org.powermock.reflect.Whitebox
import org.robolectric.Robolectric import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment import org.robolectric.RuntimeEnvironment
import org.robolectric.Shadows
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode import org.robolectric.annotation.LooperMode
import org.robolectric.shadows.ShadowIntent import org.robolectric.shadows.ShadowIntent
@ -50,6 +55,7 @@ import org.wikipedia.AppAdapter
import java.lang.reflect.Field import java.lang.reflect.Field
import java.lang.reflect.Method import java.lang.reflect.Method
import java.util.* import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap import kotlin.collections.HashMap
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
@ -57,7 +63,6 @@ import kotlin.collections.HashMap
@LooperMode(LooperMode.Mode.PAUSED) @LooperMode(LooperMode.Mode.PAUSED)
class MediaDetailFragmentUnitTests { class MediaDetailFragmentUnitTests {
private val REQUEST_CODE = 1001 private val REQUEST_CODE = 1001
private val LAST_LOCATION = "last_location_while_uploading" private val LAST_LOCATION = "last_location_while_uploading"
private val REQUEST_CODE_EDIT_DESCRIPTION = 1002 private val REQUEST_CODE_EDIT_DESCRIPTION = 1002
@ -67,6 +72,30 @@ class MediaDetailFragmentUnitTests {
private lateinit var view: View private lateinit var view: View
private lateinit var context: Context private lateinit var context: Context
private val NOMINATING_FOR_DELETION_MEDIA = "Nominating for deletion %s"
@Mock
private lateinit var deleteHelper: DeleteHelper
@Mock
private lateinit var reasonBuilder: ReasonBuilder
@Mock
private lateinit var progressBarDeletion: ProgressBar
@Mock
private lateinit var delete: Button
private var isDeleted = true
@Mock
private var reasonList: ArrayList<String>? = null
@Mock
private var reasonListEnglishMappings: ArrayList<String>? = null
@Mock @Mock
private lateinit var locationManager: LocationServiceManager private lateinit var locationManager: LocationServiceManager
@ -156,9 +185,16 @@ class MediaDetailFragmentUnitTests {
Whitebox.setInternalState(fragment, "scrollView", scrollView) Whitebox.setInternalState(fragment, "scrollView", scrollView)
categoryRecyclerView = view.findViewById(R.id.rv_categories) categoryRecyclerView = view.findViewById(R.id.rv_categories)
progressBarDeletion = view.findViewById(R.id.progressBarDeletion)
delete = view.findViewById(R.id.nominateDeletion)
Whitebox.setInternalState(fragment, "categoryRecyclerView", categoryRecyclerView) Whitebox.setInternalState(fragment, "categoryRecyclerView", categoryRecyclerView)
Whitebox.setInternalState(fragment, "media", media) Whitebox.setInternalState(fragment, "media", media)
Whitebox.setInternalState(fragment, "isDeleted", isDeleted)
Whitebox.setInternalState(fragment, "reasonList", reasonList)
Whitebox.setInternalState(fragment, "reasonListEnglishMappings", reasonListEnglishMappings)
Whitebox.setInternalState(fragment, "reasonBuilder", reasonBuilder)
Whitebox.setInternalState(fragment, "deleteHelper", deleteHelper)
Whitebox.setInternalState(fragment, "progressBar", progressBar) Whitebox.setInternalState(fragment, "progressBar", progressBar)
Whitebox.setInternalState(fragment, "progressBarEditDescription", progressBar) Whitebox.setInternalState(fragment, "progressBarEditDescription", progressBar)
Whitebox.setInternalState(fragment, "captionsListView", listView) Whitebox.setInternalState(fragment, "captionsListView", listView)
@ -175,6 +211,7 @@ class MediaDetailFragmentUnitTests {
Whitebox.setInternalState(fragment, "mediaCaption", textView) Whitebox.setInternalState(fragment, "mediaCaption", textView)
Whitebox.setInternalState(fragment, "captionLayout", linearLayout) Whitebox.setInternalState(fragment, "captionLayout", linearLayout)
Whitebox.setInternalState(fragment, "depictsLayout", linearLayout) Whitebox.setInternalState(fragment, "depictsLayout", linearLayout)
Whitebox.setInternalState(fragment, "delete", delete)
Whitebox.setInternalState(fragment, "depictionContainer", linearLayout) Whitebox.setInternalState(fragment, "depictionContainer", linearLayout)
Whitebox.setInternalState(fragment, "toDoLayout", linearLayout) Whitebox.setInternalState(fragment, "toDoLayout", linearLayout)
Whitebox.setInternalState(fragment, "dummyCategoryEditContainer", linearLayout) Whitebox.setInternalState(fragment, "dummyCategoryEditContainer", linearLayout)
@ -183,6 +220,7 @@ class MediaDetailFragmentUnitTests {
Whitebox.setInternalState(fragment, "editDescription", button) Whitebox.setInternalState(fragment, "editDescription", button)
Whitebox.setInternalState(fragment, "categoryContainer", linearLayout) Whitebox.setInternalState(fragment, "categoryContainer", linearLayout)
Whitebox.setInternalState(fragment, "categorySearchView", searchView) Whitebox.setInternalState(fragment, "categorySearchView", searchView)
Whitebox.setInternalState(fragment, "progressBarDeletion", progressBarDeletion)
Whitebox.setInternalState(fragment, "mediaDiscussion", textView) Whitebox.setInternalState(fragment, "mediaDiscussion", textView)
Whitebox.setInternalState(fragment, "locationManager", locationManager) Whitebox.setInternalState(fragment, "locationManager", locationManager)
Whitebox.setInternalState( Whitebox.setInternalState(
@ -590,6 +628,28 @@ class MediaDetailFragmentUnitTests {
method.invoke(fragment, "") method.invoke(fragment, "")
} }
@Test
@Throws(Exception::class)
fun testOnDeleteClickedNull() {
Shadows.shadowOf(Looper.getMainLooper()).idle()
val spinner = mock(Spinner::class.java)
`when`(media.imageUrl).thenReturn("test@example.com")
`when`(spinner.selectedItemPosition).thenReturn(0)
`when`(reasonListEnglishMappings?.get(spinner.selectedItemPosition)).thenReturn("TESTING")
`when`(applicationKvStore.getBoolean(String.format(MediaDetailFragment.NOMINATING_FOR_DELETION_MEDIA,media.imageUrl
))).thenReturn(true)
doReturn(Single.just(true)).`when`(deleteHelper).makeDeletion(ArgumentMatchers.any(),ArgumentMatchers.any(), ArgumentMatchers.any())
doReturn(Single.just("")).`when`(reasonBuilder).getReason(ArgumentMatchers.any(), ArgumentMatchers.any())
val method: Method = MediaDetailFragment::class.java.getDeclaredMethod(
"onDeleteClicked",
Spinner::class.java
)
method.isAccessible = true
method.invoke(fragment, spinner)
}
@Test @Test
@Throws(Exception::class) @Throws(Exception::class)
fun testForMedia() { fun testForMedia() {