mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
[GSoC] Added Unit Tests and Fixed Landscape Mode Bug (#3872)
* Fixes #3861 Use the APIs to fetch leaderboard’s based on uploads via mobile app (all time) and display it in the Leaderboard screen. * Fixed Bug - missing data in landscape mode * Added Unit Tests for Leaderboard * Added JavaDocs * Updated JavaDocs
This commit is contained in:
parent
196b9141d2
commit
5877d7a14a
8 changed files with 178 additions and 8 deletions
|
|
@ -21,7 +21,7 @@ dependencies {
|
||||||
// Utils
|
// Utils
|
||||||
implementation 'in.yuvi:http.fluent:1.3'
|
implementation 'in.yuvi:http.fluent:1.3'
|
||||||
implementation 'com.google.code.gson:gson:2.8.5'
|
implementation 'com.google.code.gson:gson:2.8.5'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.5.0'
|
implementation 'com.squareup.okhttp3:okhttp:4.8.0'
|
||||||
implementation 'com.squareup.okio:okio:2.2.2'
|
implementation 'com.squareup.okio:okio:2.2.2'
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
|
||||||
implementation 'io.reactivex.rxjava2:rxjava:2.2.3'
|
implementation 'io.reactivex.rxjava2:rxjava:2.2.3'
|
||||||
|
|
@ -50,6 +50,7 @@ dependencies {
|
||||||
testImplementation "androidx.paging:paging-common-ktx:$PAGING_VERSION"
|
testImplementation "androidx.paging:paging-common-ktx:$PAGING_VERSION"
|
||||||
implementation "androidx.paging:paging-rxjava2-ktx:$PAGING_VERSION"
|
implementation "androidx.paging:paging-rxjava2-ktx:$PAGING_VERSION"
|
||||||
implementation "androidx.recyclerview:recyclerview:1.2.0-alpha02"
|
implementation "androidx.recyclerview:recyclerview:1.2.0-alpha02"
|
||||||
|
implementation 'com.squareup.okhttp3:okhttp-ws:3.4.1'
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation 'ch.acra:acra-dialog:5.3.0'
|
implementation 'ch.acra:acra-dialog:5.3.0'
|
||||||
|
|
@ -79,7 +80,7 @@ dependencies {
|
||||||
testImplementation 'junit:junit:4.13'
|
testImplementation 'junit:junit:4.13'
|
||||||
testImplementation 'org.robolectric:robolectric:4.3'
|
testImplementation 'org.robolectric:robolectric:4.3'
|
||||||
testImplementation 'androidx.test:core:1.2.0'
|
testImplementation 'androidx.test:core:1.2.0'
|
||||||
testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1'
|
testImplementation "com.squareup.okhttp3:mockwebserver:4.8.0"
|
||||||
testImplementation "org.powermock:powermock-module-junit4:2.0.0-beta.5"
|
testImplementation "org.powermock:powermock-module-junit4:2.0.0-beta.5"
|
||||||
testImplementation "org.powermock:powermock-api-mockito2:2.0.0-beta.5"
|
testImplementation "org.powermock:powermock-api-mockito2:2.0.0-beta.5"
|
||||||
testImplementation 'org.mockito:mockito-core:2.23.0'
|
testImplementation 'org.mockito:mockito-core:2.23.0'
|
||||||
|
|
@ -94,7 +95,7 @@ dependencies {
|
||||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||||
androidTestImplementation 'androidx.test:rules:1.2.0'
|
androidTestImplementation 'androidx.test:rules:1.2.0'
|
||||||
androidTestImplementation 'androidx.annotation:annotation:1.1.0'
|
androidTestImplementation 'androidx.annotation:annotation:1.1.0'
|
||||||
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1'
|
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.8.0'
|
||||||
androidTestUtil 'androidx.test:orchestrator:1.2.0'
|
androidTestUtil 'androidx.test:orchestrator:1.2.0'
|
||||||
|
|
||||||
// Debugging
|
// Debugging
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,9 @@ public class ProfileActivity extends NavigationBaseActivity {
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the tabs for the fragments
|
||||||
|
*/
|
||||||
private void setTabs() {
|
private void setTabs() {
|
||||||
List<Fragment> fragmentList = new ArrayList<>();
|
List<Fragment> fragmentList = new ArrayList<>();
|
||||||
List<String> titleList = new ArrayList<>();
|
List<String> titleList = new ArrayList<>();
|
||||||
|
|
@ -69,7 +72,6 @@ public class ProfileActivity extends NavigationBaseActivity {
|
||||||
viewPagerAdapter.notifyDataSetChanged();
|
viewPagerAdapter.notifyDataSetChanged();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,10 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the views
|
||||||
|
* @param response Leaderboard Response Object
|
||||||
|
*/
|
||||||
private void setLeaderboardUser(LeaderboardResponse response) {
|
private void setLeaderboardUser(LeaderboardResponse response) {
|
||||||
hideProgressBar();
|
hideProgressBar();
|
||||||
avatar.setImageURI(
|
avatar.setImageURI(
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ public class LeaderboardListAdapter extends RecyclerView.Adapter<LeaderboardList
|
||||||
this.count = itemView.findViewById(R.id.user_count);
|
this.count = itemView.findViewById(R.id.user_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will return the Context
|
||||||
|
* @return Context
|
||||||
|
*/
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
return itemView.getContext();
|
return itemView.getContext();
|
||||||
}
|
}
|
||||||
|
|
@ -41,6 +45,12 @@ public class LeaderboardListAdapter extends RecyclerView.Adapter<LeaderboardList
|
||||||
this.leaderboardList = leaderboardList;
|
this.leaderboardList = leaderboardList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the onCreateViewHolder and inflates the recyclerview list item layout
|
||||||
|
* @param parent
|
||||||
|
* @param viewType
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public LeaderboardListAdapter.ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public LeaderboardListAdapter.ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
|
@ -50,6 +60,11 @@ public class LeaderboardListAdapter extends RecyclerView.Adapter<LeaderboardList
|
||||||
return new ListViewHolder(view);
|
return new ListViewHolder(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the onBindViewHolder Set the view at the specific position with the specific value
|
||||||
|
* @param holder
|
||||||
|
* @param position
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull LeaderboardListAdapter.ListViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull LeaderboardListAdapter.ListViewHolder holder, int position) {
|
||||||
TextView rank = holder.rank;
|
TextView rank = holder.rank;
|
||||||
|
|
@ -66,6 +81,10 @@ public class LeaderboardListAdapter extends RecyclerView.Adapter<LeaderboardList
|
||||||
count.setText(leaderboardList.get(position).getCategoryCount().toString());
|
count.setText(leaderboardList.get(position).getCategoryCount().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the getItemCount method
|
||||||
|
* @return the size of the recycler view list
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return leaderboardList.size();
|
return leaderboardList.size();
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<com.facebook.drawee.view.SimpleDraweeView
|
<com.facebook.drawee.view.SimpleDraweeView
|
||||||
android:id="@+id/avatar"
|
android:id="@+id/avatar"
|
||||||
|
|
@ -87,13 +87,19 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:id="@+id/leaderboard_list"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/column_names" />
|
app:layout_constraintTop_toBottomOf="@+id/column_names">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/leaderboard_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/progressBar"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import fr.free.nrw.commons.Media
|
||||||
import fr.free.nrw.commons.auth.SessionManager
|
import fr.free.nrw.commons.auth.SessionManager
|
||||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||||
import fr.free.nrw.commons.profile.achievements.FeedbackResponse
|
import fr.free.nrw.commons.profile.achievements.FeedbackResponse
|
||||||
|
import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse
|
||||||
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import media
|
import media
|
||||||
|
|
@ -54,6 +55,8 @@ class ReasonBuilderTest {
|
||||||
`when`(sessionManager?.doesAccountExist()).thenReturn(true)
|
`when`(sessionManager?.doesAccountExist()).thenReturn(true)
|
||||||
`when`(okHttpJsonApiClient!!.getAchievements(anyString()))
|
`when`(okHttpJsonApiClient!!.getAchievements(anyString()))
|
||||||
.thenReturn(Single.just(mock(FeedbackResponse::class.java)))
|
.thenReturn(Single.just(mock(FeedbackResponse::class.java)))
|
||||||
|
`when`(okHttpJsonApiClient!!.getLeaderboard(anyString(), anyString(), anyString(), anyString(), anyString()))
|
||||||
|
.thenReturn(Single.just(mock(LeaderboardResponse::class.java)))
|
||||||
|
|
||||||
val media = media(filename="test_file", dateUploaded = Date())
|
val media = media(filename="test_file", dateUploaded = Date())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
package fr.free.nrw.commons.leaderboard;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Request.Builder;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import okhttp3.mockwebserver.MockResponse;
|
||||||
|
import okhttp3.mockwebserver.MockWebServer;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class tests the Leaderboard API calls
|
||||||
|
*/
|
||||||
|
public class LeaderboardApiTest {
|
||||||
|
|
||||||
|
MockWebServer server;
|
||||||
|
private static final String TEST_USERNAME = "user";
|
||||||
|
private static final String TEST_AVATAR = "avatar";
|
||||||
|
private static final int TEST_USER_RANK = 1;
|
||||||
|
private static final int TEST_USER_COUNT = 0;
|
||||||
|
|
||||||
|
private static final String FILE_NAME = "leaderboard_sample_response.json";
|
||||||
|
private static final String ENDPOINT = "/leaderboard.py";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method initialises a Mock Server
|
||||||
|
*/
|
||||||
|
@Before
|
||||||
|
public void initTest() {
|
||||||
|
server = new MockWebServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will setup a Mock Server and load Test JSON Response File
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
String testResponseBody = convertStreamToString(getClass().getClassLoader().getResourceAsStream(FILE_NAME));
|
||||||
|
|
||||||
|
server.enqueue(new MockResponse().setBody(testResponseBody));
|
||||||
|
server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method converts a Input Stream to String
|
||||||
|
* @param is takes Input Stream of JSON File as Parameter
|
||||||
|
* @return a String with JSON data
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private static String convertStreamToString(InputStream is) throws Exception {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
sb.append(line).append("\n");
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will call the Mock Server and Test it with sample values.
|
||||||
|
* It will test the Leaderboard API call functionality and check if the object is
|
||||||
|
* being created with the correct values
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void apiTest() throws IOException {
|
||||||
|
HttpUrl httpUrl = server.url(ENDPOINT);
|
||||||
|
LeaderboardResponse response = sendRequest(new OkHttpClient(), httpUrl);
|
||||||
|
|
||||||
|
Assert.assertEquals(TEST_AVATAR, response.getAvatar());
|
||||||
|
Assert.assertEquals(TEST_USERNAME, response.getUsername());
|
||||||
|
Assert.assertEquals(Integer.valueOf(TEST_USER_RANK), response.getRank());
|
||||||
|
Assert.assertEquals(Integer.valueOf(TEST_USER_COUNT), response.getCategoryCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will call the Mock API and returns the Leaderboard Response Object
|
||||||
|
* @param okHttpClient
|
||||||
|
* @param httpUrl
|
||||||
|
* @return Leaderboard Response Object
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private LeaderboardResponse sendRequest(OkHttpClient okHttpClient, HttpUrl httpUrl)
|
||||||
|
throws IOException {
|
||||||
|
Request request = new Builder().url(httpUrl).build();
|
||||||
|
Response response = okHttpClient.newCall(request).execute();
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
return gson.fromJson(response.body().string(), LeaderboardResponse.class);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method shuts down the Mock Server
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@After
|
||||||
|
public void shutdown() throws IOException {
|
||||||
|
server.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/src/test/resources/leaderboard_sample_response.json
Normal file
19
app/src/test/resources/leaderboard_sample_response.json
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"status": 200,
|
||||||
|
"username": "user",
|
||||||
|
"category_count": 0,
|
||||||
|
"limit": null,
|
||||||
|
"avatar": "avatar",
|
||||||
|
"offset": null,
|
||||||
|
"duration": "all_time",
|
||||||
|
"leaderboard_list": [
|
||||||
|
{
|
||||||
|
"username": "user",
|
||||||
|
"category_count": 0,
|
||||||
|
"avatar": "avatar",
|
||||||
|
"rank": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"category": "used",
|
||||||
|
"rank": 1
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue