Skip to content

Commit ff05bc4

Browse files
committed
feat: Add new test to send a mail with an Attachment
1 parent 0325bd1 commit ff05bc4

5 files changed

Lines changed: 129 additions & 9 deletions

File tree

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ dependencies {
250250
androidTestImplementation(core.stdlib)
251251
androidTestImplementation(libs.espresso.contrib)
252252
androidTestImplementation(libs.espresso.core)
253+
androidTestImplementation(libs.espresso.intents)
253254
androidTestImplementation(libs.espresso.web)
254255
androidTestImplementation(libs.hamcrest)
255256
androidTestImplementation(libs.junit.ktx)

app/src/androidTest/java/com/infomaniak/mail/ui/Utils.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,37 @@
1717
*/
1818
package com.infomaniak.mail.ui
1919

20+
import android.app.Activity
21+
import android.app.Instrumentation
22+
import android.content.ClipData
23+
import android.content.Intent
24+
import android.net.Uri
2025
import android.view.View
26+
import androidx.core.content.FileProvider
2127
import androidx.test.espresso.Espresso.onView
2228
import androidx.test.espresso.ViewAssertion
2329
import androidx.test.espresso.ViewInteraction
2430
import androidx.test.espresso.action.ViewActions.click
2531
import androidx.test.espresso.action.ViewActions.typeText
2632
import androidx.test.espresso.assertion.ViewAssertions.matches
2733
import androidx.test.espresso.contrib.RecyclerViewActions
34+
import androidx.test.espresso.intent.Intents
35+
import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction
2836
import androidx.test.espresso.matcher.ViewMatchers
2937
import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA
3038
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
3139
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
3240
import androidx.test.espresso.matcher.ViewMatchers.withId
3341
import androidx.test.espresso.matcher.ViewMatchers.withText
42+
import androidx.test.platform.app.InstrumentationRegistry
3443
import com.infomaniak.mail.R
3544
import com.infomaniak.mail.ui.newMessage.ContactAdapter.ContactViewHolder
3645
import com.infomaniak.mail.utils.Env
3746
import junit.framework.AssertionFailedError
3847
import org.hamcrest.Matcher
3948
import org.hamcrest.core.AllOf.allOf
49+
import java.io.File
50+
import java.util.UUID
4051
import kotlin.time.Duration
4152
import kotlin.time.Duration.Companion.milliseconds
4253

@@ -91,4 +102,35 @@ object Utils {
91102
)
92103
).check(matches(isDisplayed()))
93104
}
105+
106+
fun interceptFilePickerIntent(fakeAttachmentUri: Uri?) {
107+
// Intercept the system file picker intent and return the fake URI
108+
Intents.intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(
109+
Instrumentation.ActivityResult(
110+
Activity.RESULT_OK,
111+
Intent().apply {
112+
clipData = ClipData.newRawUri("", fakeAttachmentUri)
113+
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
114+
},
115+
)
116+
)
117+
}
118+
119+
fun getFakeAttachment(): FakeAttachment {
120+
val targetContext = InstrumentationRegistry.getInstrumentation().targetContext
121+
val attachmentName = "ui-test-attachment-${UUID.randomUUID()}.txt"
122+
val attachmentFile = File(targetContext.cacheDir, "attachments_cache/$attachmentName").apply {
123+
parentFile?.mkdirs()
124+
writeText("UI attachment content")
125+
}
126+
val attachmentUri = FileProvider.getUriForFile(
127+
targetContext,
128+
targetContext.getString(R.string.ATTACHMENTS_AUTHORITY),
129+
attachmentFile,
130+
)
131+
132+
return FakeAttachment(attachmentName, attachmentFile, attachmentUri)
133+
}
134+
135+
data class FakeAttachment(val name: String, val file: File, val uri: Uri?)
94136
}

app/src/androidTest/java/com/infomaniak/mail/ui/login/BaseActivityTest.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@ import androidx.activity.ComponentActivity
2222
import androidx.compose.ui.test.junit4.createAndroidComposeRule
2323
import androidx.compose.ui.test.onNodeWithTag
2424
import androidx.compose.ui.test.performClick
25+
import androidx.test.espresso.Espresso.onView
26+
import androidx.test.espresso.matcher.ViewMatchers.isRoot
2527
import androidx.test.platform.app.InstrumentationRegistry
2628
import androidx.test.uiautomator.UiDevice
29+
import com.infomaniak.mail.data.LocalSettings
2730
import com.infomaniak.mail.ui.Scenarios.grantPermissions
2831
import com.infomaniak.mail.ui.Scenarios.login
2932
import com.infomaniak.mail.ui.Scenarios.toggleAnimations
33+
import com.infomaniak.mail.ui.Scenarios.waitFor
3034
import com.infomaniak.mail.utils.Env
3135
import org.junit.After
3236
import org.junit.Before
@@ -35,6 +39,7 @@ import org.junit.rules.RuleChain
3539
import org.junit.rules.Timeout
3640
import java.util.concurrent.TimeUnit
3741
import kotlin.reflect.KClass
42+
import kotlin.time.Duration.Companion.seconds
3843

3944
open class BaseActivityTest(startingActivity: KClass<out ComponentActivity>, private val loginDirectly: Boolean = true) {
4045

@@ -62,9 +67,16 @@ open class BaseActivityTest(startingActivity: KClass<out ComponentActivity>, pri
6267

6368
grantPermissions(device, permissions, packageName)
6469

70+
// Disable AI discovery bottom sheet for tests to avoid interruptions
71+
val context = InstrumentationRegistry.getInstrumentation().targetContext
72+
LocalSettings.getInstance(context).showAiDiscoveryBottomSheet = false
73+
6574
if (loginDirectly) {
6675
composeTestRule.onNodeWithTag("button_login_onboarding").performClick()
6776
login(Env.UI_TEST_ACCOUNT_EMAIL, Env.UI_TEST_ACCOUNT_PASSWORD)
77+
78+
// Waiting a little bit to let the app load
79+
onView(isRoot()).perform(waitFor(3.seconds))
6880
}
6981
}
7082

app/src/androidTest/java/com/infomaniak/mail/ui/newMessage/NewMessageActivityTest.kt

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ import androidx.recyclerview.widget.RecyclerView
2121
import androidx.test.espresso.Espresso.onView
2222
import androidx.test.espresso.action.ViewActions.click
2323
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
24-
import androidx.test.espresso.action.ViewActions.swipeDown
2524
import androidx.test.espresso.action.ViewActions.typeText
2625
import androidx.test.espresso.assertion.ViewAssertions.matches
2726
import androidx.test.espresso.contrib.RecyclerViewActions
27+
import androidx.test.espresso.intent.Intents
2828
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
2929
import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA
30+
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
3031
import androidx.test.espresso.matcher.ViewMatchers.isRoot
3132
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
3233
import androidx.test.espresso.matcher.ViewMatchers.withId
@@ -42,11 +43,15 @@ import com.infomaniak.mail.R
4243
import com.infomaniak.mail.ui.Scenarios.waitFor
4344
import com.infomaniak.mail.ui.Utils.assertRecipientInField
4445
import com.infomaniak.mail.ui.Utils.enterEmailToField
46+
import com.infomaniak.mail.ui.Utils.getFakeAttachment
47+
import com.infomaniak.mail.ui.Utils.interceptFilePickerIntent
4548
import com.infomaniak.mail.ui.Utils.onViewWithTimeout
4649
import com.infomaniak.mail.ui.login.BaseActivityTest
4750
import com.infomaniak.mail.ui.login.LoginActivity
4851
import org.hamcrest.CoreMatchers.containsString
4952
import org.hamcrest.core.AllOf.allOf
53+
import org.junit.After
54+
import org.junit.Before
5055
import org.junit.Test
5156
import org.junit.runner.RunWith
5257
import java.util.UUID
@@ -57,8 +62,20 @@ import kotlin.time.Duration.Companion.seconds
5762
@LargeTest
5863
class NewMessageActivityTest : BaseActivityTest(startingActivity = LoginActivity::class) {
5964

65+
@Before
66+
fun initIntents() = Intents.init()
67+
68+
@After
69+
fun releaseIntents() = Intents.release()
70+
6071
@Test
61-
fun sendEmail() {
72+
fun sendEmailWithAttachment() {
73+
val fakeAttachment = getFakeAttachment()
74+
interceptFilePickerIntent(fakeAttachment.uri)
75+
76+
val subjectUuid = UUID.randomUUID().toString()
77+
val subject = "UI test mail attachment #$subjectUuid"
78+
6279
onViewWithTimeout(
6380
retryInterval = 500.milliseconds,
6481
matcher = withId(R.id.newMessageFab)
@@ -67,8 +84,59 @@ class NewMessageActivityTest : BaseActivityTest(startingActivity = LoginActivity
6784
// Waiting for the view to settle
6885
onView(isRoot()).perform(waitFor(3.seconds))
6986

70-
// Dismissing the AI BottomSheet. Clicking on "Later" is not working for some reason
71-
device.click(0, 0)
87+
onView(withId(R.id.editorAttachment)).perform(click())
88+
89+
onViewWithTimeout(matcher = withId(R.id.attachmentsRecyclerView), assertion = matches(isDisplayed()))
90+
onViewWithTimeout(matcher = withText(fakeAttachment.name), assertion = matches(isDisplayed()))
91+
92+
onView(
93+
allOf(
94+
withId(R.id.chevron),
95+
isDescendantOfA(withId(R.id.toField))
96+
)
97+
).perform(click())
98+
enterEmailToField(R.id.toField, "uitest.test@ik.me", R.id.autoCompleteTo)
99+
onView(withId(R.id.subjectTextField)).perform(click(), typeText(subject), closeSoftKeyboard())
100+
onView(withId(R.id.sendButton)).perform(click())
101+
102+
// Waiting for the mail to appear in the Thread list fragment
103+
onViewWithTimeout(
104+
numberOfRetries = 20,
105+
retryInterval = 5_000.milliseconds,
106+
matcher = withId(R.id.threadsList),
107+
assertion = matches(hasDescendant(withText(subject))),
108+
)
109+
110+
// Open the received email
111+
onViewWithTimeout(
112+
retryInterval = 500.milliseconds,
113+
matcher = allOf(withId(R.id.threadsList), hasDescendant(withText(subject))),
114+
).perform(
115+
RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
116+
hasDescendant(withText(subject)),
117+
click(),
118+
)
119+
)
120+
121+
// Verify that the attachment with the correct name is displayed in the received email
122+
onViewWithTimeout(
123+
retryInterval = 2_000.milliseconds,
124+
matcher = allOf(withId(R.id.fileName), withText(fakeAttachment.name)),
125+
assertion = matches(isDisplayed()),
126+
)
127+
128+
fakeAttachment.file.delete()
129+
}
130+
131+
@Test
132+
fun sendEmail() {
133+
onViewWithTimeout(
134+
retryInterval = 500.milliseconds,
135+
matcher = withId(R.id.newMessageFab)
136+
).perform(click())
137+
138+
// Waiting for the view to settle
139+
onView(isRoot()).perform(waitFor(3.seconds))
72140

73141
// Just clicking on the toField does not work ...
74142
onView(
@@ -88,10 +156,9 @@ class NewMessageActivityTest : BaseActivityTest(startingActivity = LoginActivity
88156
onView(withId(R.id.editorWebView)).perform(click(), typeText("This is an email from UI test"), closeSoftKeyboard())
89157
onView(withId(R.id.sendButton)).perform(click())
90158

91-
onView(withId(R.id.threadsList)).perform(swipeDown())
92-
93159
// Checking if the email with a specific ID to be received
94160
onViewWithTimeout(
161+
numberOfRetries = 20,
95162
retryInterval = 5_000.milliseconds,
96163
matcher = withId(R.id.threadsList),
97164
assertion = matches(hasDescendant(withText(subject))),
@@ -116,9 +183,6 @@ class NewMessageActivityTest : BaseActivityTest(startingActivity = LoginActivity
116183
// Waiting for the view to settle
117184
onView(isRoot()).perform(waitFor(3.seconds))
118185

119-
// Dismissing the AI BottomSheet. Clicking on "Later" is not working for some reason
120-
device.click(0, 0)
121-
122186
onView(
123187
allOf(
124188
withId(R.id.chevron),

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" }
4747
# Unit tests
4848
espresso-contrib = { module = "androidx.test.espresso:espresso-contrib", version.ref = "espressoContrib" }
4949
espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" }
50+
espresso-intents = { module = "androidx.test.espresso:espresso-intents", version.ref = "espresso" }
5051
espresso-web = { module = "androidx.test.espresso:espresso-web", version.ref = "espresso" }
5152
fragment-testing = { module = "androidx.fragment:fragment-testing", version.ref = "fragmentTesting" }
5253
junit-ktx = { module = "androidx.test.ext:junit-ktx", version.ref = "junitKtx" }

0 commit comments

Comments
 (0)