Makes depicted place and category items unselectable for nearby place (#5325)

* Makes depicted place and category items unselectable for nearby place

* UploadCategoriesFragmentUnitTests.kt fixes and javadoc addition

* comment fix

* Fixes tests and hidden category appearing and dissapearing issue
This commit is contained in:
Srishti Rohatgi 2023-10-08 14:56:27 +05:30 committed by GitHub
parent 05fbfce865
commit 733c8709fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 229 additions and 66 deletions

View file

@ -136,7 +136,11 @@ class CategoriesModel @Inject constructor(
return Observable.fromIterable(categoryNames)
.map { categoryName ->
buildCategories(categoryName)
}.toList().toObservable()
}
.filter { categoryItem ->
categoryItem.name != "Hidden"
}
.toList().toObservable()
}
/**

View file

@ -293,6 +293,23 @@ public class UploadRepository {
return depictModel.getPlaceDepictions(new ArrayList<>(qids));
}
/**
* Gets the category for each unique {@link Place} associated with an {@link UploadItem}
* from {@link #getUploads()}
*
* @return a single that provides the categories
*/
public Single<List<CategoryItem>> getPlaceCategories() {
final Set<String> qids = new HashSet<>();
for (final UploadItem item : getUploads()) {
final Place place = item.getPlace();
if (place != null) {
qids.add(place.getCategory());
}
}
return Single.fromObservable(categoriesModel.getCategoriesByName(new ArrayList<>(qids)));
}
/**
* Takes depict IDs as a parameter, converts into a slash separated String and Gets DepictItem
* from the server

View file

@ -3,6 +3,8 @@ package fr.free.nrw.commons.upload;
import static fr.free.nrw.commons.contributions.ContributionController.ACTION_INTERNAL_UPLOADS;
import static fr.free.nrw.commons.utils.PermissionUtils.PERMISSIONS_STORAGE;
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
import static fr.free.nrw.commons.wikidata.WikidataConstants.SELECTED_NEARBY_PLACE;
import static fr.free.nrw.commons.wikidata.WikidataConstants.SELECTED_NEARBY_PLACE_CATEGORY;
import android.Manifest;
import android.annotation.SuppressLint;
@ -483,9 +485,17 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
}
uploadCategoriesFragment = new UploadCategoriesFragment();
if (place != null) {
Bundle categoryBundle = new Bundle();
categoryBundle.putString(SELECTED_NEARBY_PLACE_CATEGORY, place.getCategory());
uploadCategoriesFragment.setArguments(categoryBundle);
}
uploadCategoriesFragment.setCallback(this);
depictsFragment = new DepictsFragment();
Bundle placeBundle = new Bundle();
placeBundle.putParcelable(SELECTED_NEARBY_PLACE, place);
depictsFragment.setArguments(placeBundle);
depictsFragment.setCallback(this);
mediaLicenseFragment = new MediaLicenseFragment();

View file

@ -2,6 +2,7 @@ package fr.free.nrw.commons.upload.categories;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import fr.free.nrw.commons.BasePresenter;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.category.CategoryItem;
@ -80,6 +81,9 @@ public interface CategoriesContract {
*/
void updateCategories(Media media, String wikiText);
LiveData<List<CategoryItem>> getCategories();
void selectCategories();
}

View file

@ -1,6 +1,8 @@
package fr.free.nrw.commons.upload.categories
import android.text.TextUtils
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.R
import fr.free.nrw.commons.category.CategoryEditHelper
@ -36,6 +38,7 @@ class CategoriesPresenter @Inject constructor(
var view = DUMMY
private val compositeDisposable = CompositeDisposable()
private val searchTerms = PublishSubject.create<String>()
private var categoryList: MutableLiveData<List<CategoryItem>> = MutableLiveData()
/**
* Current media
*/
@ -54,8 +57,6 @@ class CategoriesPresenter @Inject constructor(
.observeOn(mainThreadScheduler)
.doOnNext {
view.showProgress(true)
view.showError(null)
view.setCategories(null)
}
.switchMap(::searchResults)
.map { repository.selectedCategories + it }
@ -63,13 +64,17 @@ class CategoriesPresenter @Inject constructor(
.observeOn(mainThreadScheduler)
.subscribe(
{
view.setCategories(it)
setCategoryListValue(it)
view.showProgress(false)
if (it.isEmpty()) {
view.showError(R.string.no_categories_found)
}
},
Timber::e
{ t: Throwable? ->
view.showProgress(false)
view.showError(R.string.no_categories_found)
Timber.e(t)
}
)
)
}
@ -92,11 +97,10 @@ class CategoriesPresenter @Inject constructor(
CategoryItem(it.name, it.description, it.thumbnail, true)
}
},
repository.searchAll(term, getImageTitleList(), repository.selectedDepictions),
{ it1, it2 ->
it1 + it2
}
)
repository.searchAll(term, getImageTitleList(), repository.selectedDepictions)
) { it1, it2 ->
it1 + it2
}
.subscribeOn(ioScheduler)
.map { it.filter { categoryItem -> !repository.containsYear(categoryItem.name)
|| categoryItem.name==term } }
@ -161,8 +165,6 @@ class CategoriesPresenter @Inject constructor(
.observeOn(mainThreadScheduler)
.doOnNext {
view.showProgress(true)
view.showError(null)
view.setCategories(null)
}
.switchMap(::searchResults)
.map { repository.selectedCategories + it }
@ -170,13 +172,17 @@ class CategoriesPresenter @Inject constructor(
.observeOn(mainThreadScheduler)
.subscribe(
{
view.setCategories(it)
setCategoryListValue(it)
view.showProgress(false)
if (it.isEmpty()) {
view.showError(R.string.no_categories_found)
}
},
Timber::e
{ t: Throwable? ->
view.showProgress(false)
view.showError(R.string.no_categories_found)
Timber.e(t)
}
)
)
}
@ -230,4 +236,53 @@ class CategoriesPresenter @Inject constructor(
view.showNoCategorySelected()
}
}
/**
* Selects each [CategoryItem] in a given list as if they were clicked by the user by calling
* [onCategoryItemClicked] for each category and adding the category to [categoryList]
*/
private fun selectNewCategories(toSelect: List<CategoryItem>) {
toSelect.forEach{
it.isSelected = true
repository.onCategoryClicked(it, media)
}
// Add the new selections to the list of category items so that the selections appear
// immediately (i.e. without any search term queries)
categoryList.value?.toMutableList()
?.let { toSelect + it }
?.distinctBy(CategoryItem::name)
?.let { setCategoryListValue(it) }
}
/**
* Livedata being used to observe category list inside
* @see UploadCategoriesFragment
* Any changes to category list reflect immediately to the adapter list
*/
override fun getCategories(): LiveData<List<CategoryItem>> {
return categoryList
}
/**
* needed for tests
*/
fun setCategoryList(categoryList: MutableLiveData<List<CategoryItem>>) {
this.categoryList = categoryList
}
/**
* needed for tests
*/
fun setCategoryListValue(categoryItems: List<CategoryItem>) {
categoryList.postValue(categoryItems)
}
override fun selectCategories() {
compositeDisposable.add(repository.placeCategories
.subscribeOn(ioScheduler)
.observeOn(mainThreadScheduler)
.subscribe(::selectNewCategories)
)
}
}

View file

@ -1,5 +1,7 @@
package fr.free.nrw.commons.upload.categories;
import static fr.free.nrw.commons.wikidata.WikidataConstants.SELECTED_NEARBY_PLACE_CATEGORY;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
@ -81,6 +83,7 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
* WikiText from the server
*/
private String wikiText;
private String nearbyPlaceCategory;
@Nullable
@Override
@ -97,9 +100,10 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
if (bundle != null) {
media = bundle.getParcelable("Existing_Categories");
wikiText = bundle.getString("WikiText");
nearbyPlaceCategory = bundle.getString(SELECTED_NEARBY_PLACE_CATEGORY);
}
init();
presenter.getCategories().observe(getViewLifecycleOwner(), this::setCategories);
}
private void init() {
@ -160,7 +164,7 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
adapter = new UploadCategoryAdapter(categoryItem -> {
presenter.onCategoryItemClicked(categoryItem);
return Unit.INSTANCE;
});
}, nearbyPlaceCategory);
rvCategories.setLayoutManager(new LinearLayoutManager(getContext()));
rvCategories.setAdapter(adapter);
}
@ -189,10 +193,9 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
@Override
public void setCategories(List<CategoryItem> categories) {
if(categories==null) {
if (categories == null) {
adapter.clear();
}
else{
} else {
adapter.setItems(categories);
}
}
@ -299,6 +302,7 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
@Override
protected void onBecameVisible() {
super.onBecameVisible();
presenter.selectCategories();
final Editable text = etSearch.getText();
if (text != null) {
presenter.searchForCategories(text.toString());

View file

@ -4,9 +4,9 @@ import fr.free.nrw.commons.category.CategoryItem
import org.jetbrains.annotations.NotNull
class UploadCategoryAdapter(
onCategoryClicked: @NotNull() (CategoryItem) -> Unit) :
onCategoryClicked: @NotNull() (CategoryItem) -> Unit, nearbyPlaceCategory: String?) :
BaseDelegateAdapter<CategoryItem>(
uploadCategoryDelegate(onCategoryClicked),
uploadCategoryDelegate(onCategoryClicked, nearbyPlaceCategory),
areItemsTheSame = { oldItem, newItem -> oldItem.name == newItem.name },
areContentsTheSame = { oldItem, newItem ->
oldItem.name == newItem.name && oldItem.isSelected == newItem.isSelected

View file

@ -6,30 +6,39 @@ import fr.free.nrw.commons.R
import fr.free.nrw.commons.category.CategoryItem
import fr.free.nrw.commons.databinding.LayoutUploadCategoriesItemBinding
fun uploadCategoryDelegate(onCategoryClicked: (CategoryItem) -> Unit) =
fun uploadCategoryDelegate(onCategoryClicked: (CategoryItem) -> Unit, nearbyPlaceCategory: String?) =
adapterDelegateViewBinding<CategoryItem, CategoryItem,
LayoutUploadCategoriesItemBinding>({ layoutInflater, root ->
LayoutUploadCategoriesItemBinding.inflate(layoutInflater, root, false)
}) {
val onClickListener = { _: View? ->
item.isSelected = !item.isSelected
binding.uploadCategoryCheckbox.isChecked = item.isSelected
onCategoryClicked(item)
if (item.name != nearbyPlaceCategory) {
item.isSelected = !item.isSelected
binding.uploadCategoryCheckbox.isChecked = item.isSelected
onCategoryClicked(item)
}
}
binding.root.setOnClickListener(onClickListener)
binding.uploadCategoryCheckbox.setOnClickListener(onClickListener)
bind {
binding.uploadCategoryCheckbox.isChecked = item.isSelected
if (item.name == nearbyPlaceCategory) {
item.isSelected = true
binding.uploadCategoryCheckbox.isChecked = true
binding.uploadCategoryCheckbox.isEnabled = false
} else {
binding.uploadCategoryCheckbox.isEnabled = true
binding.uploadCategoryCheckbox.isChecked = item.isSelected
}
binding.categoryLabel.text = item.name
if(item.thumbnail != "null") {
if (item.thumbnail != "null") {
binding.categoryImage.setImageURI(item.thumbnail)
} else {
binding.categoryImage.setActualImageResource(R.drawable.commons)
}
if(item.description != "null") {
if (item.description != "null") {
binding.categoryDescription.text = item.description
} else {
binding.categoryDescription.text = ""

View file

@ -1,5 +1,7 @@
package fr.free.nrw.commons.upload.depicts;
import static fr.free.nrw.commons.wikidata.WikidataConstants.SELECTED_NEARBY_PLACE;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
@ -29,6 +31,7 @@ import fr.free.nrw.commons.R;
import fr.free.nrw.commons.contributions.ContributionsFragment;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.media.MediaDetailFragment;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText;
import fr.free.nrw.commons.upload.UploadActivity;
import fr.free.nrw.commons.upload.UploadBaseFragment;
@ -83,6 +86,7 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
* Determines each encounter of edit depicts
*/
private int count;
private Place nearbyPlace;
@Nullable
@Override
@ -99,6 +103,7 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
Bundle bundle = getArguments();
if (bundle != null) {
media = bundle.getParcelable("Existing_Depicts");
nearbyPlace = bundle.getParcelable(SELECTED_NEARBY_PLACE);
}
init();
@ -111,8 +116,7 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
private void init() {
if (media == null) {
depictsTitle
.setText(getString(R.string.step_count, callback.getIndexInViewFlipper(this) + 1,
depictsTitle.setText(String.format(getString(R.string.step_count), callback.getIndexInViewFlipper(this) + 1,
callback.getTotalNumberOfSteps(), getString(R.string.depicts_step_title)));
} else {
depictsTitle.setText(R.string.edit_depictions);
@ -156,12 +160,12 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
adapter = new UploadDepictsAdapter(categoryItem -> {
presenter.onDepictItemClicked(categoryItem);
return Unit.INSTANCE;
});
}, nearbyPlace);
} else {
adapter = new UploadDepictsAdapter(item -> {
presenter.onDepictItemClicked(item);
return Unit.INSTANCE;
});
}, nearbyPlace);
}
depictsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
depictsRecyclerView.setAdapter(adapter);

View file

@ -101,11 +101,10 @@ class DepictsPresenter @Inject constructor(
it.commonsCategories, true, it.id)
}
},
repository.searchAllEntities(querystring),
{ it1, it2 ->
it1 + it2
}
)
repository.searchAllEntities(querystring)
) { it1, it2 ->
it1 + it2
}
.subscribeOn(ioScheduler)
.map { repository.selectedDepictions + it + recentDepictedItemList + controller.loadFavoritesItems() }
.map { it.filterNot { item -> WikidataDisambiguationItems.isDisambiguationItem(item.instanceOfs) } }

View file

@ -1,11 +1,12 @@
package fr.free.nrw.commons.upload.depicts
import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.upload.categories.BaseDelegateAdapter
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
class UploadDepictsAdapter(onDepictsClicked: (DepictedItem) -> Unit) :
class UploadDepictsAdapter(onDepictsClicked: (DepictedItem) -> Unit, nearbyPlace: Place?) :
BaseDelegateAdapter<DepictedItem>(
uploadDepictsDelegate(onDepictsClicked),
uploadDepictsDelegate(onDepictsClicked, nearbyPlace),
areItemsTheSame = { oldItem, newItem -> oldItem.id == newItem.id },
areContentsTheSame = { itemA, itemB -> itemA.isSelected == itemB.isSelected}
)

View file

@ -6,29 +6,39 @@ import android.view.View
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import fr.free.nrw.commons.R
import fr.free.nrw.commons.databinding.LayoutUploadDepictsItemBinding
import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
fun uploadDepictsDelegate(onDepictClicked: (DepictedItem) -> Unit) =
fun uploadDepictsDelegate(onDepictClicked: (DepictedItem) -> Unit, nearbyPlace: Place?) =
adapterDelegateViewBinding<DepictedItem, DepictedItem, LayoutUploadDepictsItemBinding>({ layoutInflater, parent ->
LayoutUploadDepictsItemBinding.inflate(layoutInflater, parent, false)
}) {
val onClickListener = { _: View? ->
item.isSelected = !item.isSelected
binding.depictCheckbox.isChecked = item.isSelected
onDepictClicked(item)
if (item.name != nearbyPlace?.name) {
item.isSelected = !item.isSelected
binding.depictCheckbox.isChecked = item.isSelected
onDepictClicked(item)
}
}
binding.root.setOnClickListener(onClickListener)
binding.depictCheckbox.setOnClickListener(onClickListener)
bind {
binding.depictCheckbox.isChecked = item.isSelected
binding.depictsLabel.text = item.name
binding.description.text = item.description
val imageUrl = item.imageUrl
if (TextUtils.isEmpty(imageUrl)) {
binding.depictedImage.setActualImageResource(R.drawable.ic_wikidata_logo_24dp)
if (item.name == nearbyPlace?.name) {
item.isSelected = true
binding.depictCheckbox.isChecked = true
binding.depictCheckbox.isEnabled = false
} else {
binding.depictedImage.setImageURI(Uri.parse(imageUrl))
binding.depictCheckbox.isEnabled = true
binding.depictCheckbox.isChecked = item.isSelected
}
binding.depictsLabel.text = item.name
binding.description.text = item.description
val imageUrl = item.imageUrl
if (TextUtils.isEmpty(imageUrl)) {
binding.depictedImage.setActualImageResource(R.drawable.ic_wikidata_logo_24dp)
} else {
binding.depictedImage.setImageURI(Uri.parse(imageUrl))
}
}
}

View file

@ -3,4 +3,6 @@ package fr.free.nrw.commons.wikidata;
public class WikidataConstants {
public static final String PLACE_OBJECT = "place";
public static final String BOOKMARKS_ITEMS = "bookmarks.items";
public static final String SELECTED_NEARBY_PLACE = "selected.nearby.place";
public static final String SELECTED_NEARBY_PLACE_CATEGORY = "selected.nearby.place.category";
}

View file

@ -1,8 +1,10 @@
package fr.free.nrw.commons.upload
import androidx.lifecycle.MutableLiveData
import categoryItem
import com.nhaarman.mockitokotlin2.*
import fr.free.nrw.commons.R
import fr.free.nrw.commons.category.CategoryItem
import fr.free.nrw.commons.repository.UploadRepository
import fr.free.nrw.commons.upload.categories.CategoriesContract
import fr.free.nrw.commons.upload.categories.CategoriesPresenter
@ -13,9 +15,9 @@ import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.powermock.reflect.Whitebox
import java.lang.reflect.Method
/**
* The class contains unit test cases for CategoriesPresenter
*/
@ -39,7 +41,7 @@ class CategoriesPresenterTest {
MockitoAnnotations.initMocks(this)
testScheduler = TestScheduler()
categoriesPresenter = CategoriesPresenter(repository, testScheduler, testScheduler)
categoriesPresenter.onAttachView(view)
}
@Test
@ -51,7 +53,7 @@ class CategoriesPresenterTest {
@Test
@Throws(Exception::class)
fun `Test onAttachViewWithMedia when media is not null`() {
Whitebox.setInternalState(categoriesPresenter, "media", media())
categoriesPresenter.onAttachViewWithMedia(view, media())
whenever(repository.getCategories(repository.selectedExistingCategories))
.thenReturn(Observable.just(mutableListOf(categoryItem())))
whenever(repository.searchAll("mock", emptyList(), repository.selectedDepictions))
@ -68,6 +70,11 @@ class CategoriesPresenterTest {
*/
@Test
fun `searchForCategories combines selection and search results without years distinctly`() {
categoriesPresenter.onAttachView(view)
val liveData = MutableLiveData<List<CategoryItem>>()
categoriesPresenter.setCategoryList(liveData)
categoriesPresenter.setCategoryListValue(listOf(
categoryItem("selected", "", "", true)))
val nonEmptyCaptionUploadItem = mock<UploadItem>()
whenever(nonEmptyCaptionUploadItem.uploadMediaDetails)
.thenReturn(listOf(UploadMediaDetail(captionText = "nonEmpty")))
@ -80,7 +87,7 @@ class CategoriesPresenterTest {
emptyCaptionUploadItem
)
)
whenever(repository.searchAll("test", listOf("nonEmpty"), repository.selectedDepictions))
whenever(repository.searchAll(any(), any(), any()))
.thenReturn(
Observable.just(
listOf(
@ -96,25 +103,34 @@ class CategoriesPresenterTest {
categoriesPresenter.searchForCategories("test")
testScheduler.triggerActions()
verify(view).showProgress(true)
verify(view).showError(null)
verify(view).setCategories(null)
verify(view).setCategories(listOf(
categoryItem("selected", "", "", true)))
verify(view).showProgress(false)
verifyNoMoreInteractions(view)
}
@Test
fun `searchForCategoriesTest sets Error when list is empty`() {
whenever(repository.uploads).thenReturn(listOf())
whenever(repository.searchAll(any(), any(), any())).thenReturn(Observable.just(listOf()))
categoriesPresenter.onAttachView(view)
val liveData = MutableLiveData<List<CategoryItem>>()
categoriesPresenter.setCategoryList(liveData)
// Arrange
val query = "testQuery"
val emptyCategories = ArrayList<CategoryItem>()
liveData.postValue(emptyCategories)
whenever(repository.searchAll(any(), any(), any()))
.thenReturn(Observable.just(emptyCategories))
whenever(repository.selectedCategories).thenReturn(listOf())
categoriesPresenter.searchForCategories("test")
categoriesPresenter.searchForCategories(query)
testScheduler.triggerActions()
val method: Method = CategoriesPresenter::class.java.getDeclaredMethod(
"searchResults",
String::class.java
)
method.isAccessible = true
method.invoke(categoriesPresenter, query)
verify(view).showProgress(true)
verify(view).showError(null)
verify(view).setCategories(null)
verify(view).setCategories(listOf())
verify(view).showProgress(false)
verify(view).showError(R.string.no_categories_found)
verifyNoMoreInteractions(view)
@ -125,6 +141,7 @@ class CategoriesPresenterTest {
*/
@Test
fun `verifyCategories with non empty selection goes to next screen`() {
categoriesPresenter.onAttachView(view)
val item = categoryItem()
whenever(repository.selectedCategories).thenReturn(listOf(item))
categoriesPresenter.verifyCategories()
@ -134,6 +151,7 @@ class CategoriesPresenterTest {
@Test
fun `verifyCategories with empty selection show no category selected`() {
categoriesPresenter.onAttachView(view)
whenever(repository.selectedCategories).thenReturn(listOf())
categoriesPresenter.verifyCategories()
verify(view).showNoCategorySelected()
@ -156,6 +174,7 @@ class CategoriesPresenterTest {
@Test
fun testUpdateCategories() {
categoriesPresenter.onAttachView(view)
categoriesPresenter.updateCategories(media(), "[[Category:Test]]")
}
}

View file

@ -145,9 +145,34 @@ class UploadCategoriesFragmentUnitTests {
@Test
@Throws(Exception::class)
fun testOnViewCreated() {
fun testInitMethod() {
val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod(
"init"
)
Shadows.shadowOf(Looper.getMainLooper()).idle()
fragment.onViewCreated(view, null)
method.isAccessible = true
method.invoke(fragment)
}
@Test
@Throws(Exception::class)
fun `Test init when media is non null`() {
Whitebox.setInternalState(fragment, "media", media)
val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod(
"init"
)
method.isAccessible = true
method.invoke(fragment)
}
@Test
@Throws(Exception::class)
fun testFragmentOnBecameVisible() {
val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod(
"onBecameVisible"
)
method.isAccessible = true
method.invoke(fragment)
}
@Test