-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[WIP] Implemented Espresso tests for upload with multilingual descriptions #2830
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0463dd1
586836b
c198b3d
053a765
052ee6a
fc3545d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,7 +76,11 @@ | |
-keepattributes SourceFile,LineNumberTable | ||
-keepattributes *Annotation* | ||
|
||
# --- /recycler view --- | ||
-keep class androidx.recyclerview.widget.RecyclerView { | ||
public androidx.recyclerview.widget.RecyclerView$ViewHolder findViewHolderForPosition(int); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you actually verify this proguard config? I think the inner class you mention in the declaration that it's a class and the method should have a return type. Examples: https://www.guardsquare.com/en/products/proguard/manual/examples |
||
} | ||
# --- Parcelable --- | ||
-keepclassmembers class * implements android.os.Parcelable { | ||
static ** CREATOR; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,11 +8,13 @@ import android.graphics.Bitmap | |
import android.net.Uri | ||
import android.os.Environment | ||
import android.view.View | ||
import androidx.recyclerview.widget.RecyclerView | ||
import androidx.test.espresso.Espresso.onView | ||
import androidx.test.espresso.NoMatchingViewException | ||
import androidx.test.espresso.action.ViewActions.click | ||
import androidx.test.espresso.action.ViewActions.replaceText | ||
import androidx.test.espresso.assertion.ViewAssertions.matches | ||
import androidx.test.espresso.contrib.RecyclerViewActions | ||
import androidx.test.espresso.intent.Intents | ||
import androidx.test.espresso.intent.Intents.intended | ||
import androidx.test.espresso.intent.Intents.intending | ||
|
@@ -24,6 +26,8 @@ import androidx.test.rule.ActivityTestRule | |
import androidx.test.rule.GrantPermissionRule | ||
import androidx.test.runner.AndroidJUnit4 | ||
import fr.free.nrw.commons.auth.LoginActivity | ||
import fr.free.nrw.commons.upload.DescriptionsAdapter | ||
import fr.free.nrw.commons.util.MyViewAction | ||
import fr.free.nrw.commons.utils.ConfigUtils | ||
import org.hamcrest.core.AllOf.allOf | ||
import org.junit.After | ||
|
@@ -65,68 +69,154 @@ class UploadTest { | |
} | ||
UITestHelper.skipWelcome() | ||
UITestHelper.loginUser() | ||
saveToInternalStorage() | ||
} | ||
|
||
@After | ||
fun teardown() { | ||
Intents.release() | ||
} | ||
|
||
private fun saveToInternalStorage() { | ||
val bitmapImage = randomBitmap | ||
@Test | ||
fun testUploadWithDescription() { | ||
if (!ConfigUtils.isBetaFlavour()) { | ||
throw Error("This test should only be run in Beta!") | ||
} | ||
|
||
// path to /data/data/yourapp/app_data/imageDir | ||
val mypath = File(Environment.getExternalStorageDirectory(), "image.jpg") | ||
setupSingleUpload("image.jpg") | ||
|
||
Timber.d("Filepath: %s", mypath.path) | ||
openGallery() | ||
|
||
Timber.d("Absolute Filepath: %s", mypath.absolutePath) | ||
// Validate that an intent to get an image is sent | ||
intended(allOf(hasAction(Intent.ACTION_GET_CONTENT), hasType("image/*"))) | ||
|
||
// Create filename with the current time (to prevent overwrites) | ||
val dateFormat = SimpleDateFormat("yyMMdd-hhmmss") | ||
val commonsFileName = "MobileTest " + dateFormat.format(Date()) | ||
|
||
// Try to dismiss the error, if there is one (probably about duplicate files on Commons) | ||
dismissWarning("Yes") | ||
|
||
onView(allOf<View>(isDisplayed(), withId(R.id.et_title))) | ||
.perform(replaceText(commonsFileName)) | ||
|
||
onView(allOf<View>(isDisplayed(), withId(R.id.description_item_edit_text))) | ||
.perform(replaceText(commonsFileName)) | ||
|
||
|
||
onView(allOf(isDisplayed(), withId(R.id.btn_next))) | ||
.perform(click()) | ||
|
||
UITestHelper.sleep(5000) | ||
dismissWarning("Yes") | ||
|
||
UITestHelper.sleep(3000) | ||
|
||
onView(allOf(isDisplayed(), withId(R.id.et_search))) | ||
.perform(replaceText("Uploaded with Mobile/Android Tests")) | ||
|
||
UITestHelper.sleep(3000) | ||
|
||
var fos: FileOutputStream? = null | ||
try { | ||
fos = FileOutputStream(mypath) | ||
// Use the compress method on the BitMap object to write image to the OutputStream | ||
bitmapImage.compress(Bitmap.CompressFormat.JPEG, 100, fos) | ||
} catch (e: Exception) { | ||
e.printStackTrace() | ||
} finally { | ||
try { | ||
fos?.close() | ||
} catch (e: IOException) { | ||
e.printStackTrace() | ||
} | ||
onView(allOf(isDisplayed(), UITestHelper.first(withParent(withId(R.id.rv_categories))))) | ||
.perform(click()) | ||
} catch (ignored: NoMatchingViewException) { | ||
} | ||
|
||
onView(allOf(isDisplayed(), withId(R.id.btn_next))) | ||
.perform(click()) | ||
|
||
dismissWarning("Yes, Submit") | ||
|
||
UITestHelper.sleep(500) | ||
|
||
onView(allOf(isDisplayed(), withId(R.id.btn_submit))) | ||
.perform(click()) | ||
|
||
UITestHelper.sleep(10000) | ||
|
||
val fileUrl = "https://commons.wikimedia.beta.wmflabs.org/wiki/File:" + | ||
commonsFileName.replace(' ', '_') + ".jpg" | ||
Timber.i("File should be uploaded to $fileUrl") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a way to verify that the file upload request is triggered with the right arguments? Without making sure that the inputs are properly read, I'm not sure what exactly this is testing, besides that buttons can be clcked without crashing |
||
} | ||
|
||
private fun dismissWarning(warningText: String) { | ||
try { | ||
onView(withText(warningText)) | ||
.check(matches(isDisplayed())) | ||
.perform(click()) | ||
} catch (ignored: NoMatchingViewException) { | ||
} | ||
} | ||
|
||
@Test | ||
fun uploadTest() { | ||
fun testUploadWithoutDescription() { | ||
if (!ConfigUtils.isBetaFlavour()) { | ||
throw Error("This test should only be run in Beta!") | ||
} | ||
|
||
// Uri to return by our mock gallery selector | ||
// Requires file 'image.jpg' to be placed at root of file structure | ||
val imageUri = Uri.parse("file://mnt/sdcard/image.jpg") | ||
setupSingleUpload("image.jpg") | ||
|
||
// Build a result to return from the Camera app | ||
val intent = Intent() | ||
intent.data = imageUri | ||
val result = ActivityResult(Activity.RESULT_OK, intent) | ||
openGallery() | ||
|
||
// Stub out the File picker. When an intent is sent to the File picker, this tells | ||
// Espresso to respond with the ActivityResult we just created | ||
intending(allOf(hasAction(Intent.ACTION_GET_CONTENT), hasType("image/*"))).respondWith(result) | ||
// Validate that an intent to get an image is sent | ||
intended(allOf(hasAction(Intent.ACTION_GET_CONTENT), hasType("image/*"))) | ||
|
||
// Open FAB | ||
onView(allOf<View>(withId(R.id.fab_plus), isDisplayed())) | ||
// Create filename with the current time (to prevent overwrites) | ||
val dateFormat = SimpleDateFormat("yyMMdd-hhmmss") | ||
val commonsFileName = "MobileTest " + dateFormat.format(Date()) | ||
|
||
// Try to dismiss the error, if there is one (probably about duplicate files on Commons) | ||
dismissWarning("Yes") | ||
|
||
onView(allOf<View>(isDisplayed(), withId(R.id.et_title))) | ||
.perform(replaceText(commonsFileName)) | ||
|
||
onView(allOf(isDisplayed(), withId(R.id.btn_next))) | ||
.perform(click()) | ||
|
||
// Click gallery | ||
onView(allOf<View>(withId(R.id.fab_gallery), isDisplayed())) | ||
UITestHelper.sleep(10000) | ||
dismissWarning("Yes") | ||
|
||
UITestHelper.sleep(3000) | ||
|
||
onView(allOf(isDisplayed(), withId(R.id.et_search))) | ||
.perform(replaceText("Test")) | ||
|
||
UITestHelper.sleep(3000) | ||
|
||
try { | ||
onView(allOf(isDisplayed(), UITestHelper.first(withParent(withId(R.id.rv_categories))))) | ||
.perform(click()) | ||
} catch (ignored: NoMatchingViewException) { | ||
} | ||
|
||
onView(allOf(isDisplayed(), withId(R.id.btn_next))) | ||
.perform(click()) | ||
|
||
dismissWarning("Yes, Submit") | ||
|
||
UITestHelper.sleep(500) | ||
|
||
onView(allOf(isDisplayed(), withId(R.id.btn_submit))) | ||
.perform(click()) | ||
|
||
UITestHelper.sleep(10000) | ||
|
||
val fileUrl = "https://commons.wikimedia.beta.wmflabs.org/wiki/File:" + | ||
commonsFileName.replace(' ', '_') + ".jpg" | ||
Timber.i("File should be uploaded to $fileUrl") | ||
} | ||
|
||
@Test | ||
fun testUploadWithMultilingualDescription() { | ||
if (!ConfigUtils.isBetaFlavour()) { | ||
throw Error("This test should only be run in Beta!") | ||
} | ||
|
||
setupSingleUpload("image.jpg") | ||
|
||
openGallery() | ||
|
||
// Validate that an intent to get an image is sent | ||
intended(allOf(hasAction(Intent.ACTION_GET_CONTENT), hasType("image/*"))) | ||
|
||
|
@@ -135,14 +225,28 @@ class UploadTest { | |
val commonsFileName = "MobileTest " + dateFormat.format(Date()) | ||
|
||
// Try to dismiss the error, if there is one (probably about duplicate files on Commons) | ||
dismissWarning("Yes") | ||
dismissWarningDialog() | ||
|
||
onView(allOf<View>(isDisplayed(), withId(R.id.et_title))) | ||
.perform(replaceText(commonsFileName)) | ||
|
||
onView(allOf<View>(isDisplayed(), withId(R.id.description_item_edit_text))) | ||
.perform(replaceText(commonsFileName)) | ||
onView(withId(R.id.rv_descriptions)).perform( | ||
RecyclerViewActions | ||
.actionOnItemAtPosition<DescriptionsAdapter.ViewHolder>(0, | ||
MyViewAction.typeTextInChildViewWithId(R.id.description_item_edit_text, "Test description"))) | ||
|
||
onView(withId(R.id.btn_add_description)) | ||
.perform(click()) | ||
|
||
onView(withId(R.id.rv_descriptions)).perform( | ||
RecyclerViewActions | ||
.actionOnItemAtPosition<DescriptionsAdapter.ViewHolder>(1, | ||
MyViewAction.selectSpinnerItemInChildViewWithId(R.id.spinner_description_languages, 2))) | ||
|
||
onView(withId(R.id.rv_descriptions)).perform( | ||
RecyclerViewActions | ||
.actionOnItemAtPosition<DescriptionsAdapter.ViewHolder>(1, | ||
MyViewAction.typeTextInChildViewWithId(R.id.description_item_edit_text, "Description"))) | ||
|
||
onView(allOf(isDisplayed(), withId(R.id.btn_next))) | ||
.perform(click()) | ||
|
@@ -153,12 +257,12 @@ class UploadTest { | |
UITestHelper.sleep(3000) | ||
|
||
onView(allOf(isDisplayed(), withId(R.id.et_search))) | ||
.perform(replaceText("Uploaded with Mobile/Android Tests")) | ||
.perform(replaceText("Test")) | ||
|
||
UITestHelper.sleep(3000) | ||
|
||
try { | ||
onView(allOf(isDisplayed(), withParent(withId(R.id.rv_categories)))) | ||
onView(allOf(isDisplayed(), UITestHelper.first(withParent(withId(R.id.rv_categories))))) | ||
.perform(click()) | ||
} catch (ignored: NoMatchingViewException) { | ||
} | ||
|
@@ -180,12 +284,69 @@ class UploadTest { | |
Timber.i("File should be uploaded to $fileUrl") | ||
} | ||
|
||
private fun dismissWarning(warningText: String) { | ||
private fun setupSingleUpload(imageName: String) { | ||
saveToInternalStorage(imageName) | ||
singleImageIntent(imageName) | ||
} | ||
|
||
private fun saveToInternalStorage(imageName: String) { | ||
val bitmapImage = randomBitmap | ||
|
||
// path to /data/data/yourapp/app_data/imageDir | ||
val mypath = File(Environment.getExternalStorageDirectory(), imageName) | ||
|
||
Timber.d("Filepath: %s", mypath.path) | ||
|
||
Timber.d("Absolute Filepath: %s", mypath.absolutePath) | ||
|
||
var fos: FileOutputStream? = null | ||
try { | ||
onView(withText(warningText)) | ||
fos = FileOutputStream(mypath) | ||
// Use the compress method on the BitMap object to write image to the OutputStream | ||
bitmapImage.compress(Bitmap.CompressFormat.JPEG, 100, fos) | ||
} catch (e: Exception) { | ||
e.printStackTrace() | ||
} finally { | ||
try { | ||
fos?.close() | ||
} catch (e: IOException) { | ||
e.printStackTrace() | ||
} | ||
|
||
} | ||
} | ||
|
||
private fun singleImageIntent(imageName: String) { | ||
// Uri to return by our mock gallery selector | ||
// Requires file 'image.jpg' to be placed at root of file structure | ||
val imageUri = Uri.parse("file://mnt/sdcard/$imageName") | ||
|
||
// Build a result to return from the Camera app | ||
val intent = Intent() | ||
intent.data = imageUri | ||
val result = ActivityResult(Activity.RESULT_OK, intent) | ||
|
||
// Stub out the File picker. When an intent is sent to the File picker, this tells | ||
// Espresso to respond with the ActivityResult we just created | ||
intending(allOf(hasAction(Intent.ACTION_GET_CONTENT), hasType("image/*"))).respondWith(result) | ||
} | ||
|
||
private fun dismissWarningDialog() { | ||
try { | ||
onView(withText("Yes")) | ||
.check(matches(isDisplayed())) | ||
.perform(click()) | ||
} catch (ignored: NoMatchingViewException) { | ||
} | ||
} | ||
|
||
private fun openGallery() { | ||
// Open FAB | ||
onView(allOf<View>(withId(R.id.fab_plus), isDisplayed())) | ||
.perform(click()) | ||
|
||
// Click gallery | ||
onView(allOf<View>(withId(R.id.fab_gallery), isDisplayed())) | ||
.perform(click()) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be the opening comment, without
/
, and this one would go at the end of the recycler view - related block