Skip to content

Commit db5290e

Browse files
domdomeggmaskaravivek
authored andcommitted
Upload UI tests (commons-app#2626)
* Fix SettingsActivityTest * Add test in SettingsActivity to set recent upload limit to 0 * Add tests to recent upload limit setting * Simplify activity test rule * Add UploadTest * Log the URL where the file should be uploaded * Generate an image file before testing uploads * With runtime permissions * With automatic login * With automatic login * Get test credentials from travis
1 parent 1040c2d commit db5290e

File tree

3 files changed

+225
-9
lines changed

3 files changed

+225
-9
lines changed

app/build.gradle

+5
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ dependencies {
6666
// Android testing
6767
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION"
6868
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
69+
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
6970
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.1'
7071
androidTestImplementation 'androidx.test:runner:1.1.1'
7172
androidTestImplementation 'androidx.test:rules:1.1.1'
@@ -193,6 +194,8 @@ android {
193194
buildConfigField "String", "BOOKMARK_AUTHORITY", "\"fr.free.nrw.commons.bookmarks.contentprovider\""
194195
buildConfigField "String", "BOOKMARK_LOCATIONS_AUTHORITY", "\"fr.free.nrw.commons.bookmarks.locations.contentprovider\""
195196
buildConfigField "String", "COMMIT_SHA", "\"" + getBuildVersion().toString() + "\""
197+
buildConfigField "String", "TEST_USERNAME", "\"" + System.getenv("test_user_name") + "\""
198+
buildConfigField "String", "TEST_PASSWORD", "\"" + System.getenv("test_user_password") + "\""
196199

197200
dimension 'tier'
198201
}
@@ -223,6 +226,8 @@ android {
223226
buildConfigField "String", "BOOKMARK_AUTHORITY", "\"fr.free.nrw.commons.beta.bookmarks.contentprovider\""
224227
buildConfigField "String", "BOOKMARK_LOCATIONS_AUTHORITY", "\"fr.free.nrw.commons.beta.bookmarks.locations.contentprovider\""
225228
buildConfigField "String", "COMMIT_SHA", "\"" + getBuildVersion().toString() + "\""
229+
buildConfigField "String", "TEST_USERNAME", "\"" + System.getenv("test_user_name") + "\""
230+
buildConfigField "String", "TEST_PASSWORD", "\"" + System.getenv("test_user_password") + "\""
226231

227232
dimension 'tier'
228233
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package fr.free.nrw.commons;
2+
3+
import android.Manifest;
4+
import android.app.Activity;
5+
import android.app.Instrumentation.ActivityResult;
6+
import android.content.Context;
7+
import android.content.ContextWrapper;
8+
import android.content.Intent;
9+
import android.graphics.Bitmap;
10+
import android.net.Uri;
11+
import android.os.Environment;
12+
import android.view.autofill.AutofillManager;
13+
14+
import org.junit.Before;
15+
import org.junit.Rule;
16+
import org.junit.Test;
17+
import org.junit.runner.RunWith;
18+
19+
import java.io.File;
20+
import java.io.FileOutputStream;
21+
import java.io.IOException;
22+
import java.text.SimpleDateFormat;
23+
import java.util.Date;
24+
import java.util.Random;
25+
26+
import androidx.test.espresso.NoMatchingViewException;
27+
import androidx.test.espresso.intent.rule.IntentsTestRule;
28+
import androidx.test.filters.LargeTest;
29+
import androidx.test.platform.app.InstrumentationRegistry;
30+
import androidx.test.rule.ActivityTestRule;
31+
import androidx.test.rule.GrantPermissionRule;
32+
import androidx.test.runner.AndroidJUnit4;
33+
import fr.free.nrw.commons.auth.LoginActivity;
34+
import fr.free.nrw.commons.utils.ConfigUtils;
35+
import timber.log.Timber;
36+
37+
import static androidx.test.espresso.Espresso.onView;
38+
import static androidx.test.espresso.action.ViewActions.clearText;
39+
import static androidx.test.espresso.action.ViewActions.click;
40+
import static androidx.test.espresso.action.ViewActions.replaceText;
41+
import static androidx.test.espresso.action.ViewActions.typeText;
42+
import static androidx.test.espresso.assertion.ViewAssertions.matches;
43+
import static androidx.test.espresso.intent.Intents.intended;
44+
import static androidx.test.espresso.intent.Intents.intending;
45+
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
46+
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasType;
47+
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
48+
import static androidx.test.espresso.matcher.ViewMatchers.withId;
49+
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
50+
import static androidx.test.espresso.matcher.ViewMatchers.withText;
51+
import static org.hamcrest.core.AllOf.allOf;
52+
53+
@LargeTest
54+
@RunWith(AndroidJUnit4.class)
55+
public class UploadTest {
56+
@Rule
57+
public GrantPermissionRule permissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE,
58+
Manifest.permission.ACCESS_FINE_LOCATION);
59+
60+
@Rule
61+
public ActivityTestRule activityRule = new IntentsTestRule<>(LoginActivity.class);
62+
63+
@Before
64+
public void setup() {
65+
66+
saveToInternalStorage();
67+
}
68+
69+
private void saveToInternalStorage() {
70+
Bitmap bitmapImage = getRandomBitmap();
71+
Context applicationContext = InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
72+
ContextWrapper cw = new ContextWrapper(applicationContext);
73+
// path to /data/data/yourapp/app_data/imageDir
74+
File mypath = new File(Environment.getExternalStorageDirectory(), "image.jpg");
75+
76+
Timber.d("Filepath: %s", mypath.getPath());
77+
78+
Timber.d("Absolute Filepath: %s", mypath.getAbsolutePath());
79+
80+
FileOutputStream fos = null;
81+
try {
82+
fos = new FileOutputStream(mypath);
83+
// Use the compress method on the BitMap object to write image to the OutputStream
84+
bitmapImage.compress(Bitmap.CompressFormat.JPEG, 100, fos);
85+
} catch (Exception e) {
86+
e.printStackTrace();
87+
} finally {
88+
try {
89+
if (fos != null) {
90+
fos.close();
91+
}
92+
} catch (IOException e) {
93+
e.printStackTrace();
94+
}
95+
}
96+
}
97+
98+
private Bitmap getRandomBitmap() {
99+
Random random = new Random();
100+
Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
101+
bitmap.eraseColor(random.nextInt(255));
102+
return bitmap;
103+
}
104+
105+
private void getToMainActivity() {
106+
try {
107+
//Skip tutorial
108+
onView(withId(R.id.finishTutorialButton))
109+
.perform(click());
110+
111+
//Perform Login
112+
onView(withId(R.id.loginUsername))
113+
.perform(clearText(), typeText(BuildConfig.TEST_USERNAME));
114+
onView(withId(R.id.loginPassword))
115+
.perform(clearText(), typeText(BuildConfig.TEST_PASSWORD));
116+
onView(withId(R.id.loginButton))
117+
.perform(click());
118+
} catch (NoMatchingViewException ignored) {
119+
}
120+
}
121+
122+
@Test
123+
public void uploadTest() {
124+
if (!ConfigUtils.isBetaFlavour()) {
125+
throw new Error("This test should only be run in Beta!");
126+
}
127+
128+
getToMainActivity();
129+
130+
// Uri to return by our mock gallery selector
131+
// Requires file 'image.jpg' to be placed at root of file structure
132+
Uri imageUri = Uri.parse("file://mnt/sdcard/image.jpg");
133+
134+
// Build a result to return from the Camera app
135+
Intent intent = new Intent();
136+
intent.setData(imageUri);
137+
ActivityResult result = new ActivityResult(Activity.RESULT_OK, intent);
138+
139+
// Stub out the File picker. When an intent is sent to the File picker, this tells
140+
// Espresso to respond with the ActivityResult we just created
141+
intending(allOf(hasAction(Intent.ACTION_GET_CONTENT), hasType("image/*"))).respondWith(result);
142+
143+
// Open FAB
144+
onView(allOf(withId(R.id.fab_plus), isDisplayed()))
145+
.perform(click());
146+
147+
// Click gallery
148+
onView(allOf(withId(R.id.fab_gallery), isDisplayed()))
149+
.perform(click());
150+
151+
// Validate that an intent to get an image is sent
152+
intended(allOf(hasAction(Intent.ACTION_GET_CONTENT), hasType("image/*")));
153+
154+
// Create filename with the current time (to prevent overwrites)
155+
SimpleDateFormat dateFormat = new SimpleDateFormat("yyMMdd-hhmmss");
156+
String commonsFileName = "MobileTest " + dateFormat.format(new Date());
157+
158+
// Try to dismiss the error, if there is one (probably about duplicate files on Commons)
159+
try {
160+
onView(withText("Yes"))
161+
.check(matches(isDisplayed()))
162+
.perform(click());
163+
} catch (NoMatchingViewException ignored) {}
164+
165+
onView(allOf(withId(R.id.description_item_edit_text), withParent(withParent(withId(R.id.image_title_container)))))
166+
.perform(replaceText(commonsFileName));
167+
168+
onView(withId(R.id.bottom_card_next))
169+
.perform(click());
170+
171+
try {
172+
Thread.sleep(500);
173+
} catch (InterruptedException e) {
174+
e.printStackTrace();
175+
}
176+
177+
onView(withId(R.id.category_search))
178+
.perform(replaceText("Uploaded with Mobile/Android Tests"));
179+
180+
try {
181+
Thread.sleep(3000);
182+
} catch (InterruptedException e) {
183+
e.printStackTrace();
184+
}
185+
186+
onView(withParent(withId(R.id.categories)))
187+
.perform(click());
188+
189+
onView(withId(R.id.category_next))
190+
.perform(click());
191+
192+
try {
193+
Thread.sleep(500);
194+
} catch (InterruptedException e) {
195+
e.printStackTrace();
196+
}
197+
198+
onView(withId(R.id.submit))
199+
.perform(click());
200+
201+
try {
202+
Thread.sleep(10000);
203+
} catch (InterruptedException e) {
204+
e.printStackTrace();
205+
}
206+
207+
String fileUrl = "https://commons.wikimedia.beta.wmflabs.org/wiki/File:" +
208+
commonsFileName.replace(' ', '_') + ".jpg";
209+
Timber.i("File should be uploaded to " + fileUrl);
210+
}
211+
}

app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java

+9-9
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@
55
import android.content.Intent;
66
import android.content.pm.PackageManager;
77
import android.os.Bundle;
8-
import com.google.android.material.tabs.TabLayout;
9-
import androidx.fragment.app.Fragment;
10-
import androidx.fragment.app.FragmentManager;
11-
import androidx.fragment.app.FragmentPagerAdapter;
12-
import androidx.core.view.GravityCompat;
13-
import androidx.viewpager.widget.ViewPager;
14-
import androidx.drawerlayout.widget.DrawerLayout;
158
import android.view.LayoutInflater;
169
import android.view.Menu;
1710
import android.view.MenuInflater;
@@ -20,18 +13,24 @@
2013
import android.widget.ImageView;
2114
import android.widget.TextView;
2215

16+
import com.google.android.material.tabs.TabLayout;
17+
2318
import java.util.List;
2419

2520
import javax.inject.Inject;
26-
import javax.inject.Named;
2721

22+
import androidx.core.view.GravityCompat;
23+
import androidx.drawerlayout.widget.DrawerLayout;
24+
import androidx.fragment.app.Fragment;
25+
import androidx.fragment.app.FragmentManager;
26+
import androidx.fragment.app.FragmentPagerAdapter;
27+
import androidx.viewpager.widget.ViewPager;
2828
import butterknife.BindView;
2929
import butterknife.ButterKnife;
3030
import fr.free.nrw.commons.BuildConfig;
3131
import fr.free.nrw.commons.R;
3232
import fr.free.nrw.commons.auth.AuthenticatedActivity;
3333
import fr.free.nrw.commons.auth.SessionManager;
34-
import fr.free.nrw.commons.kvstore.JsonKvStore;
3534
import fr.free.nrw.commons.location.LocationServiceManager;
3635
import fr.free.nrw.commons.nearby.NearbyFragment;
3736
import fr.free.nrw.commons.nearby.NearbyNotificationCardView;
@@ -464,6 +463,7 @@ private void updateContributionFragmentTabContent(boolean isContributionsListFra
464463

465464
@Override
466465
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
466+
Timber.d(data.toString());
467467
super.onActivityResult(requestCode, resultCode, data);
468468
controller.handleActivityResult(this, requestCode, resultCode, data);
469469
}

0 commit comments

Comments
 (0)