In app feedback (#4845)

* rebase

* Progress

* Completed UI and POST Request

* removed invalid string resource

* Removed unused code & Added string resources

* Resolved Code style issues

* Javadoc for getters & setters

* Codestyle fixes

* Minor Fixes

* wip

* Tests

* Comments

* Fixed Tests

* Minor changes

* minor change

* Comments

* Minor Fixes

* fixed tests

* Removed Butterknife

* Fixed tests

* Removed Unecessary strings

* Minor chnages

* Minor fix

* Minor changes

* Minor changes

* Implemented Suggestions

* Removed Redundant Toast
This commit is contained in:
Devarsh Mavani 2022-03-20 08:40:25 +05:30 committed by GitHub
parent 00760ba1c6
commit fa0370438b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 777 additions and 5 deletions

View file

@ -0,0 +1,95 @@
package fr.free.nrw.commons.feedback;
import android.content.Context;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.feedback.model.Feedback;
import fr.free.nrw.commons.utils.LangCodeUtils;
import java.util.Locale;
/**
* Creates a wikimedia recognizable format
* from feedback information
*/
public class FeedbackContentCreator {
private StringBuilder stringBuilder;
private Feedback feedback;
private Context context;
public FeedbackContentCreator(Context context, Feedback feedback) {
this.feedback = feedback;
this.context = context;
init();
}
/**
* Initializes the string buffer object to append content from feedback object
*/
public void init() {
// Localization is not needed here, because this ends up on a page where developers read the feedback, so English is the most convenient.
stringBuilder = new StringBuilder();
stringBuilder.append("== ");
stringBuilder.append("Feedback from ~~~ for version ");
stringBuilder.append(feedback.getVersion());
stringBuilder.append(" ==");
stringBuilder.append("\n");
stringBuilder.append(feedback.getTitle());
stringBuilder.append("\n");
stringBuilder.append("\n");
if (feedback.getApiLevel() != null) {
stringBuilder.append("* ");
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
Locale.ENGLISH).getString(R.string.api_level));
stringBuilder.append(": ");
stringBuilder.append(feedback.getApiLevel());
stringBuilder.append("\n");
}
if (feedback.getAndroidVersion() != null) {
stringBuilder.append("* ");
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
Locale.ENGLISH).getString(R.string.android_version));
stringBuilder.append(": ");
stringBuilder.append(feedback.getAndroidVersion());
stringBuilder.append("\n");
}
if (feedback.getDeviceManufacturer() != null) {
stringBuilder.append("* ");
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
Locale.ENGLISH).getString(R.string.device_manufacturer));
stringBuilder.append(": ");
stringBuilder.append(feedback.getDeviceManufacturer());
stringBuilder.append("\n");
}
if (feedback.getDeviceModel() != null) {
stringBuilder.append("* ");
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
Locale.ENGLISH).getString(R.string.device_model));
stringBuilder.append(": ");
stringBuilder.append(feedback.getDeviceModel());
stringBuilder.append("\n");
}
if (feedback.getDevice() != null) {
stringBuilder.append("* ");
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
Locale.ENGLISH).getString(R.string.device_name));
stringBuilder.append(": ");
stringBuilder.append(feedback.getDevice());
stringBuilder.append("\n");
}
if (feedback.getNetworkType() != null) {
stringBuilder.append("* ");
stringBuilder.append(LangCodeUtils.getLocalizedResources(context,
Locale.ENGLISH).getString(R.string.network_type));
stringBuilder.append(": ");
stringBuilder.append(feedback.getNetworkType());
stringBuilder.append("\n");
}
stringBuilder.append("~~~~");
stringBuilder.append("\n");
}
@Override
public String toString() {
return stringBuilder.toString();
}
}

View file

@ -0,0 +1,66 @@
package fr.free.nrw.commons.feedback;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.databinding.DialogFeedbackBinding;
import fr.free.nrw.commons.feedback.model.Feedback;
import fr.free.nrw.commons.utils.ConfigUtils;
import fr.free.nrw.commons.utils.DeviceInfoUtil;
/**
* Feedback dialog that asks user for message and
* other device specifications
*/
public class FeedbackDialog extends Dialog {
DialogFeedbackBinding dialogFeedbackBinding;
private OnFeedbackSubmitCallback onFeedbackSubmitCallback;
public FeedbackDialog(Context context, OnFeedbackSubmitCallback onFeedbackSubmitCallback) {
super(context);
this.onFeedbackSubmitCallback = onFeedbackSubmitCallback;
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dialogFeedbackBinding = DialogFeedbackBinding.inflate(getLayoutInflater());
final View view = dialogFeedbackBinding.getRoot();
setContentView(view);
dialogFeedbackBinding.btnSubmitFeedback.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
submitFeedback();
}
});
}
/**
* When the button is clicked, it will create a feedback object
* and give a callback to calling activity/fragment
*/
void submitFeedback() {
if(dialogFeedbackBinding.feedbackItemEditText.getText().toString().equals("")) {
dialogFeedbackBinding.feedbackItemEditText.setError(getContext().getString(R.string.enter_description));
return;
}
String appVersion = ConfigUtils.getVersionNameWithSha(getContext());
String androidVersion = dialogFeedbackBinding.androidVersionCheckbox.isChecked() ? DeviceInfoUtil.getAndroidVersion() : null;
String apiLevel = dialogFeedbackBinding.apiLevelCheckbox.isChecked() ? DeviceInfoUtil.getAPILevel() : null;
String deviceManufacturer = dialogFeedbackBinding.deviceManufacturerCheckbox.isChecked() ? DeviceInfoUtil.getDeviceManufacturer() : null;
String deviceModel = dialogFeedbackBinding.deviceModelCheckbox.isChecked() ? DeviceInfoUtil.getDeviceModel() : null;
String deviceName = dialogFeedbackBinding.deviceNameCheckbox.isChecked() ? DeviceInfoUtil.getDevice() : null;
String networkType = dialogFeedbackBinding.networkTypeCheckbox.isChecked() ? DeviceInfoUtil.getConnectionType(getContext()).toString() : null;
Feedback feedback = new Feedback(appVersion, apiLevel
, dialogFeedbackBinding.feedbackItemEditText.getText().toString()
, androidVersion, deviceModel, deviceManufacturer, deviceName, networkType);
onFeedbackSubmitCallback.onFeedbackSubmit(feedback);
dismiss();
}
}

View file

@ -0,0 +1,15 @@
package fr.free.nrw.commons.feedback;
import fr.free.nrw.commons.feedback.model.Feedback;
/**
* This interface is used to provide callback
* from Feedback dialog whenever submit button is clicked
*/
public interface OnFeedbackSubmitCallback {
/**
* callback function, called when user clicks on submit
*/
void onFeedbackSubmit(Feedback feedback);
}

View file

@ -0,0 +1,171 @@
package fr.free.nrw.commons.feedback.model;
/**
* Pojo class for storing information that are required while uploading a feedback
*/
public class Feedback {
/**
* Version of app
*/
private String version;
/**
* API level of user's phone
*/
private String apiLevel;
/**
* Title/Description entered by user
*/
private String title;
/**
* Android version of user's device
*/
private String androidVersion;
/**
* Device Model of user's device
*/
private String deviceModel;
/**
* Device manufacturer name
*/
private String deviceManufacturer;
/**
* Device name stored on user's device
*/
private String device;
/**
* network type user is having (Ex: Wifi)
*/
private String networkType;
public Feedback(final String version, final String apiLevel, final String title, final String androidVersion,
final String deviceModel, final String deviceManufacturer, final String device, final String networkType
) {
this.version = version;
this.apiLevel = apiLevel;
this.title = title;
this.androidVersion = androidVersion;
this.deviceModel = deviceModel;
this.deviceManufacturer = deviceManufacturer;
this.device = device;
this.networkType = networkType;
}
/**
* Get the version from which this piece of feedback is being sent.
* Ex: 3.0.1
*/
public String getVersion() {
return version;
}
/**
* Set the version of app to given version
*/
public void setVersion(final String version) {
this.version = version;
}
/**
* gets api level of device
* Ex: 28
*/
public String getApiLevel() {
return apiLevel;
}
/**
* sets api level value to given value
*/
public void setApiLevel(final String apiLevel) {
this.apiLevel = apiLevel;
}
/**
* gets feedback text entered by user
*/
public String getTitle() {
return title;
}
/**
* sets feedback text
*/
public void setTitle(final String title) {
this.title = title;
}
/**
* gets android version of device
* Ex: 9
*/
public String getAndroidVersion() {
return androidVersion;
}
/**
* sets value of android version
*/
public void setAndroidVersion(final String androidVersion) {
this.androidVersion = androidVersion;
}
/**
* get device model of current device
* Ex: Redmi 6 Pro
*/
public String getDeviceModel() {
return deviceModel;
}
/**
* sets value of device model to a given value
*/
public void setDeviceModel(final String deviceModel) {
this.deviceModel = deviceModel;
}
/**
* get device manufacturer of user's device
* Ex: Redmi
*/
public String getDeviceManufacturer() {
return deviceManufacturer;
}
/**
* set device manufacturer value to a given value
*/
public void setDeviceManufacturer(final String deviceManufacturer) {
this.deviceManufacturer = deviceManufacturer;
}
/**
* get device name of user's device
*/
public String getDevice() {
return device;
}
/**
* sets device name value to a given value
*/
public void setDevice(final String device) {
this.device = device;
}
/**
* get network type of user's network
* Ex: wifi
*/
public String getNetworkType() {
return networkType;
}
/**
* sets network type to a given value
*/
public void setNetworkType(final String networkType) {
this.networkType = networkType;
}
}

View file

@ -24,13 +24,23 @@ import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.actions.PageEditClient;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.feedback.FeedbackContentCreator;
import fr.free.nrw.commons.feedback.model.Feedback;
import fr.free.nrw.commons.feedback.FeedbackDialog;
import fr.free.nrw.commons.feedback.OnFeedbackSubmitCallback;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.logging.CommonsLogSender;
import fr.free.nrw.commons.profile.ProfileActivity;
import fr.free.nrw.commons.review.ReviewActivity;
import fr.free.nrw.commons.settings.SettingsActivity;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import java.util.concurrent.Callable;
import javax.inject.Inject;
import javax.inject.Named;
import timber.log.Timber;
@ -47,6 +57,10 @@ public class MoreBottomSheetFragment extends BottomSheetDialogFragment {
@Inject @Named("default_preferences")
JsonKvStore store;
@Inject
@Named("commons-page-edit")
PageEditClient pageEditClient;
@Nullable
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
@ -104,7 +118,44 @@ public class MoreBottomSheetFragment extends BottomSheetDialogFragment {
@OnClick(R.id.more_feedback)
public void onFeedbackClicked() {
showAlertDialog();
showFeedbackDialog();
}
/**
* Creates and shows a dialog asking feedback from users
*/
private void showFeedbackDialog() {
new FeedbackDialog(getContext(), new OnFeedbackSubmitCallback() {
@Override
public void onFeedbackSubmit(Feedback feedback) {
uploadFeedback(feedback);
}
}).show();
}
/**
* uploads feedback data on the server
*/
void uploadFeedback(Feedback feedback) {
FeedbackContentCreator feedbackContentCreator = new FeedbackContentCreator(getContext(), feedback);
Single<Boolean> single =
pageEditClient.prependEdit("Commons:Mobile_app/Feedback", feedbackContentCreator.toString(), "Summary")
.flatMapSingle(result -> Single.just(result))
.firstOrError();
Single.defer((Callable<SingleSource<Boolean>>) () -> single)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aBoolean -> {
if (aBoolean) {
Toast.makeText(getContext(), getString(R.string.thanks_feedback), Toast.LENGTH_SHORT)
.show();
} else {
Toast.makeText(getContext(), getString(R.string.error_feedback),
Toast.LENGTH_SHORT).show();
}
});
}
/**

View file

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_margin="@dimen/dimen_6"
android:padding="@dimen/tiny_gap"
android:textSize="24sp"
android:textColor="@color/black"
android:text="@string/navigation_item_feedback"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/feedback_item_edit_text_input_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_6"
android:layout_marginEnd="@dimen/dimen_6"
>
<fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText
android:id="@+id/feedback_item_edit_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/your_feedback"
android:imeOptions="actionNext|flagNoExtractUi"
android:lines="4"
android:inputType="text|textMultiLine"
app:allowFormatting="false" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:layout_margin="@dimen/dimen_6"
android:padding="4dp"
android:textSize="21sp"
android:textColor="@color/black"
android:text="@string/select_feedback_data_choice"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/api_level_checkbox"
android:text="@string/api_level"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkMark="?android:attr/textCheckMark"
android:checked="true"
android:gravity="center_vertical"
android:padding="@dimen/tiny_gap"
android:layout_margin="@dimen/dimen_6"
/>
<CheckBox
android:id="@+id/android_version_checkbox"
android:text="@string/android_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkMark="?android:attr/textCheckMark"
android:checked="true"
android:gravity="center_vertical"
android:padding="@dimen/tiny_gap"
android:layout_margin="@dimen/dimen_6"
/>
<CheckBox
android:id="@+id/device_manufacturer_checkbox"
android:text="@string/device_manufacturer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkMark="?android:attr/textCheckMark"
android:checked="true"
android:gravity="center_vertical"
android:padding="@dimen/tiny_gap"
android:layout_margin="@dimen/dimen_6"
/>
<CheckBox
android:id="@+id/device_model_checkbox"
android:text="@string/device_model"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkMark="?android:attr/textCheckMark"
android:checked="true"
android:gravity="center_vertical"
android:padding="@dimen/tiny_gap"
android:layout_margin="@dimen/dimen_6"
/>
<CheckBox
android:id="@+id/device_name_checkbox"
android:text="@string/device_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkMark="?android:attr/textCheckMark"
android:checked="true"
android:gravity="center_vertical"
android:padding="@dimen/tiny_gap"
android:layout_margin="@dimen/dimen_6"
/>
<CheckBox
android:id="@+id/network_type_checkbox"
android:text="@string/network_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkMark="?android:attr/textCheckMark"
android:checked="true"
android:gravity="center_vertical"
android:padding="@dimen/tiny_gap"
android:layout_margin="@dimen/dimen_6"
/>
<Button
android:id="@+id/btn_submit_feedback"
android:textColor="@color/white"
android:layout_marginBottom="@dimen/dimen_10"
android:gravity="center"
android:layout_gravity="center"
android:text="@string/submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>

View file

@ -650,5 +650,4 @@
<string name="feedback_sharing_data_alert">Од оваа порака отстранете ги сите информации што не сакате да бидат јавни. Воедно, имајте на ум дека вашата е-поштенска адреса од која испраќате, името и профилна слика ќе бидат јавно видливи.</string>
<string name="achievements_unavailable_beta">Достигнувањата се достапни само во производниот вкус.</string>
<string name="leaderboard_unavailable_beta">Предводниците се достапни само во производниот вкус.</string>
<string name="copyright_popup">Подигајте само слики што сте ги направиле самите. Подигачите на слики заштитени со авторски права ќе бидат блокирани. Ова важи и за бета-верзијата. Ви благодариме што го испробувате прилогот!</string>
</resources>

View file

@ -693,4 +693,15 @@ Upload your first media by tapping on the add button.</string>
<string name="achievements_unavailable_beta">Achievements are only available in the prod flavor, please check the developer documentation.</string>
<string name="leaderboard_unavailable_beta">The leaderboard is only available in the prod flavor, please check the developer documentation.</string>
<string name="copyright_popup">Please only upload pictures you have taken by yourself. Uploaders of copyrighted images will be blocked. This applies to the beta flavor too. Thank you for testing the app!</string>
<string name="select_feedback_data_choice">Please deselect any information that you are not comfortable sharing publicly.</string>
<string name="api_level">API level</string>
<string name="android_version">Android version</string>
<string name="device_manufacturer">Device manufacturer</string>
<string name="device_model">Device model</string>
<string name="device_name">Device name</string>
<string name="network_type">Network type</string>
<string name="thanks_feedback">Thanks for giving feedback</string>
<string name="error_feedback">Error while sending feedback</string>
<string name="enter_description">What is your feedback?</string>
<string name="your_feedback">Your feedback</string>
</resources>

View file

@ -0,0 +1,44 @@
package fr.free.nrw.commons.feedback
import android.content.Context
import fr.free.nrw.commons.FakeContextWrapper
import fr.free.nrw.commons.TestAppAdapter
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.feedback.model.Feedback
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.doReturn
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import org.wikipedia.AppAdapter
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class)
class FeedbackContentCreatorUnitTests {
private lateinit var creator: FeedbackContentCreator
private lateinit var feedback: Feedback
@Mock
private lateinit var context: Context
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
AppAdapter.set(TestAppAdapter())
context = RuntimeEnvironment.application.applicationContext
}
@Test
fun testToString() {
feedback = Feedback("123", "apiLevel", "title", "androidVersion", "deviceModel", "mfg", "deviceName", "wifi")
creator = FeedbackContentCreator(context, feedback)
Assert.assertNotNull(creator.toString())
}
}

View file

@ -0,0 +1,87 @@
package fr.free.nrw.commons.feedback
import android.content.Context
import android.os.Looper.getMainLooper
import android.text.Editable
import com.nhaarman.mockitokotlin2.doReturn
import fr.free.nrw.commons.TestAppAdapter
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.contributions.MainActivity
import fr.free.nrw.commons.databinding.DialogFeedbackBinding
import fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText
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.`when`
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import org.powermock.reflect.Whitebox
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode
import org.wikipedia.AppAdapter
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class)
@LooperMode(LooperMode.Mode.PAUSED)
class FeedbackDialogTests {
private lateinit var dialogFeedbackBinding: DialogFeedbackBinding
@Mock
private val onFeedbackSubmitCallback: OnFeedbackSubmitCallback? = null
private lateinit var dialog: FeedbackDialog
private lateinit var context: Context
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
context = RuntimeEnvironment.application.applicationContext
AppAdapter.set(TestAppAdapter())
val activity = Robolectric.buildActivity(MainActivity::class.java).create().get()
dialog = FeedbackDialog(activity.applicationContext, onFeedbackSubmitCallback)
dialogFeedbackBinding = DialogFeedbackBinding.inflate(dialog.layoutInflater)
dialog.show()
Whitebox.setInternalState(dialog, "onFeedbackSubmitCallback", onFeedbackSubmitCallback)
Whitebox.setInternalState(dialog, "dialogFeedbackBinding", dialogFeedbackBinding)
}
@Test
fun testOnCreate() {
dialog.onCreate(null)
}
@Test
fun testSubmitFeedbackError() {
val editable = mock(Editable::class.java)
val ed = mock(PasteSensitiveTextInputEditText::class.java)
Whitebox.setInternalState(dialogFeedbackBinding, "feedbackItemEditText", ed)
`when`(ed?.text).thenReturn(editable)
doReturn(editable).`when`(dialogFeedbackBinding.feedbackItemEditText)?.text
doReturn("").`when`(editable).toString()
dialog.submitFeedback()
}
@Test
fun testSubmitFeedback() {
shadowOf(getMainLooper()).idle()
val editable: Editable = mock(Editable::class.java)
val ed = mock(PasteSensitiveTextInputEditText::class.java)
Whitebox.setInternalState(dialogFeedbackBinding, "feedbackItemEditText", ed)
`when`(ed?.text).thenReturn(editable)
`when`(editable.toString()).thenReturn("1234")
Assert.assertEquals(dialogFeedbackBinding.feedbackItemEditText?.text.toString(), "1234")
dialog.submitFeedback()
}
}

View file

@ -0,0 +1,65 @@
package fr.free.nrw.commons.feedback
import fr.free.nrw.commons.feedback.model.Feedback
import org.junit.Assert
import org.junit.Before
import org.junit.Test
class FeedbackUnitTests {
private lateinit var feedback: Feedback
@Before
fun setup() {
feedback = Feedback("123", "apiLevel", "title", "androidVersion", "deviceModel", "mfg", "deviceName", "wifi")
}
@Test
fun testVersion() {
feedback.version = "456"
Assert.assertEquals(feedback.version, "456")
}
@Test
fun testApiLevel() {
Assert.assertEquals(feedback.apiLevel, "apiLevel")
feedback.apiLevel = "29"
Assert.assertEquals(feedback.apiLevel, "29")
}
@Test
fun testTitle() {
feedback.title = "myTitle"
Assert.assertEquals(feedback.title, "myTitle")
}
@Test
fun testAndroidVersion() {
feedback.androidVersion = "PIE"
Assert.assertEquals(feedback.androidVersion, "PIE")
}
@Test
fun testDeviceModel() {
feedback.deviceModel = "My Device"
Assert.assertEquals(feedback.deviceModel, "My Device")
}
@Test
fun testDeviceMfg() {
feedback.deviceManufacturer = "MYCOMPANY"
Assert.assertEquals(feedback.deviceManufacturer, "MYCOMPANY")
}
@Test
fun testDeviceName() {
feedback.device = "my_name"
Assert.assertEquals(feedback.device, "my_name")
}
@Test
fun testNetworkType() {
feedback.networkType = "network"
Assert.assertEquals(feedback.networkType, "network")
}
}

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.navtab
import android.app.Dialog
import android.content.ComponentName
import android.content.Context
import android.content.Intent
@ -7,20 +8,33 @@ import android.net.Uri
import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import fr.free.nrw.commons.CommonsApplication
import fr.free.nrw.commons.R
import fr.free.nrw.commons.TestAppAdapter
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.actions.PageEditClient
import fr.free.nrw.commons.feedback.FeedbackDialog
import fr.free.nrw.commons.feedback.model.Feedback
import fr.free.nrw.commons.kvstore.JsonKvStore
import fr.free.nrw.commons.profile.ProfileActivity
import fr.free.nrw.commons.utils.ConfigUtils
import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.SingleSource
import io.reactivex.functions.Function
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations
import org.powermock.reflect.Whitebox
import org.robolectric.Robolectric
@ -31,7 +45,10 @@ import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode
import org.robolectric.shadows.ShadowActivity
import org.robolectric.shadows.ShadowAlertDialog
import org.robolectric.shadows.ShadowDialog
import org.wikipedia.AppAdapter
import java.lang.reflect.Method
import java.util.concurrent.Callable
@RunWith(RobolectricTestRunner::class)
@ -51,10 +68,14 @@ class MoreBottomSheetFragmentUnitTests {
@Mock
private lateinit var morePeerReview: TextView
@Mock
private lateinit var pageEditClient: PageEditClient
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
context = RuntimeEnvironment.application.applicationContext
AppAdapter.set(TestAppAdapter())
activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get()
fragment = MoreBottomSheetFragment()
@ -65,6 +86,7 @@ class MoreBottomSheetFragmentUnitTests {
Whitebox.setInternalState(fragment, "store", store)
Whitebox.setInternalState(fragment, "morePeerReview", morePeerReview)
Whitebox.setInternalState(fragment, "pageEditClient", pageEditClient)
`when`(store.getBoolean(CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED)).thenReturn(
true
@ -101,8 +123,18 @@ class MoreBottomSheetFragmentUnitTests {
fun testOnFeedbackClicked() {
Shadows.shadowOf(Looper.getMainLooper()).idle()
fragment.onFeedbackClicked()
val dialog: AlertDialog = ShadowAlertDialog.getLatestDialog() as AlertDialog
Assert.assertEquals(dialog.isShowing, true)
ShadowDialog.getLatestDialog().findViewById<Button>(R.id.btn_submit_feedback).performClick()
}
@Test
@Throws(Exception::class)
fun testUploadFeedback() {
Shadows.shadowOf(Looper.getMainLooper()).idle()
val feedback = mock(Feedback::class.java)
val observable: Observable<Boolean> = Observable.just(false)
val observable2: Observable<Boolean> = Observable.just(true)
doReturn(observable, observable2).`when`(pageEditClient).prependEdit(anyString(), anyString(), anyString())
fragment.uploadFeedback(feedback)
}
@Test