Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added apps/parent/src/androidTest/assets/test_video.mp4
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package com.instructure.parentapp.ui.e2e.compose

import android.os.SystemClock.sleep
import android.util.Log
import androidx.test.espresso.Espresso
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector
import com.instructure.canvas.espresso.FeatureCategory
import com.instructure.canvas.espresso.Priority
import com.instructure.canvas.espresso.TestCategory
Expand All @@ -42,6 +47,7 @@ import com.instructure.parentapp.utils.extensions.seedData
import com.instructure.parentapp.utils.extensions.tokenLogin
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Test
import java.io.File
import java.util.Date

@HiltAndroidTest
Expand Down Expand Up @@ -619,4 +625,217 @@ class InboxE2ETest: ParentComposeTest() {
inboxPage.openConversation(expectedSubjectEvent)
inboxDetailsPage.assertMessageDisplayed(expectedMessage)
}

@E2E
@Test
@TestMetaData(Priority.IMPORTANT, FeatureCategory.INBOX, TestCategory.E2E)
fun testInboxMessageReplyWithVideoAttachmentE2E() {

Log.d(PREPARATION_TAG, "Seeding data.")
val data = seedData(students = 1, teachers = 1, parents = 1, courses = 1)
val parent = data.parentsList[0]
val teacher = data.teachersList[0]
val course = data.coursesList[0]

Log.d(PREPARATION_TAG, "Copy mp4 file to Downloads folder for attachment.")
val videoFileName = "test_video.mp4"
setupFileOnDevice(videoFileName)
File(InstrumentationRegistry.getInstrumentation().targetContext.cacheDir, "file_upload").deleteRecursively()

val conversationSubject = "Need Document Help"
val conversationBody = "Can you please send me the course document?"
Log.d(PREPARATION_TAG, "Create a conversation from '${teacher.name}' to '${parent.name}'.")
val seededConversation = ConversationsApi.createConversationForCourse(token = teacher.token, courseId = course.id, recipients = listOf(parent.id.toString()), subject = conversationSubject, body = conversationBody)[0]

Log.d(STEP_TAG, "Login with user: '${parent.name}', login id: '${parent.loginId}'.")
tokenLogin(parent)
dashboardPage.waitForRender()

Log.d(STEP_TAG, "Open the Left Side Navigation Drawer menu.")
dashboardPage.openLeftSideMenu()

Log.d(STEP_TAG, "Open 'Inbox' menu.")
leftSideNavigationDrawerPage.clickInbox()

Log.d(ASSERTION_TAG, "Assert that the conversation is displayed.")
inboxPage.assertConversationDisplayed(seededConversation.subject)

Log.d(STEP_TAG, "Open the conversation.")
inboxPage.openConversation(seededConversation.subject)

Log.d(ASSERTION_TAG, "Assert that the '${conversationSubject}' and '${conversationBody}' are displayed.")
inboxDetailsPage.assertConversationSubject(conversationSubject)
inboxDetailsPage.assertMessageDisplayed(conversationBody)

Log.d(STEP_TAG, "Click Reply button to respond to the conversation.")
inboxDetailsPage.pressOverflowMenuItemForConversation("Reply")

val replyMessage = "Sure! Here is the document."
Log.d(STEP_TAG, "Type reply message: '$replyMessage'")
inboxComposeMessagePage.typeBody(replyMessage)

Log.d(ASSERTION_TAG, "Assert that send button is enabled after typing message.")
inboxComposeMessagePage.assertIfSendButtonState(true)

Log.d(STEP_TAG, "Click attachment button to open file picker dialog.")
inboxComposeMessagePage.clickAttachmentButton()

Log.d(PREPARATION_TAG, "Simulate file picker intent (again).")
Intents.init()
try {
stubFilePickerIntent(videoFileName)
fileChooserPage.chooseDevice()
}
finally {
Intents.release()
}

Log.d(STEP_TAG, "Click OKAY button to confirm file selection.")
fileChooserPage.clickOkay()

Log.d(ASSERTION_TAG, "Assert that the video file is displayed as attached in the screen.")
inboxComposeMessagePage.assertAttachmentDisplayed(videoFileName)

Log.d(STEP_TAG, "Send the reply message with attachment.")
sleep(2000) //Wait for attachment to finish uploading
inboxComposeMessagePage.pressSendButton()

Log.d(ASSERTION_TAG, "Assert that the reply message, attachment, and original message are displayed in the conversation.")
inboxDetailsPage.assertMessageDisplayed(replyMessage)
inboxDetailsPage.assertAttachmentDisplayed(videoFileName)
inboxDetailsPage.assertMessageDisplayed(conversationBody)

Log.d(STEP_TAG, "Click on the video attachment to download it.")
inboxDetailsPage.clickAttachment(videoFileName)

Log.d(STEP_TAG, "Wait for download to complete.")
sleep(5000)

Log.d(STEP_TAG, "Open the Notification bar.")
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
device.openNotification()

retryWithIncreasingDelay(times = 10, maxDelay = 3000) {
Log.d(STEP_TAG, "Find download notification.")
val downloadNotification = device.findObject(UiSelector().textContains(videoFileName).className("android.widget.TextView"))

Log.d(ASSERTION_TAG, "Assert that 'Download complete' text is displayed in notification.")
val downloadCompleteText = device.findObject(UiSelector().textContains("Download complete"))
assert(downloadCompleteText.exists()) { "Download complete text not found in notification" }

Log.d(ASSERTION_TAG, "Assert that file name '$videoFileName' is displayed in notification.")
assert(downloadNotification.exists()) { "File name '$videoFileName' not found in notification" }
}

Log.d(STEP_TAG, "Close notification shade.")
device.pressBack()

Log.d(STEP_TAG, "Assert that the '${conversationSubject}' is displayed.")
inboxDetailsPage.assertConversationSubject(conversationSubject)

Log.d(STEP_TAG, "Navigate back to inbox.")
Espresso.pressBack()

Log.d(ASSERTION_TAG, "Assert that the conversation is still displayed in inbox.")
inboxPage.assertConversationDisplayed(seededConversation.subject)
}

@E2E
@Test
@TestMetaData(Priority.IMPORTANT, FeatureCategory.INBOX, TestCategory.E2E)
fun testInboxMessageForwardE2E() {

Log.d(PREPARATION_TAG, "Seeding data.")
val data = seedData(students = 1, teachers = 1, parents = 2, courses = 1)
val parent1 = data.parentsList[0]
val parent2 = data.parentsList[1]
val teacher = data.teachersList[0]
val course = data.coursesList[0]

val conversationSubject = "Important Announcement"
val conversationBody = "Please review this."
Log.d(PREPARATION_TAG, "Create a conversation from '${teacher.name}' to '${parent1.name}'.")
val seededConversation = ConversationsApi.createConversationForCourse(token = teacher.token, courseId = course.id, recipients = listOf(parent1.id.toString()), subject = conversationSubject, body = conversationBody)[0]

Log.d(STEP_TAG, "Login with user: '${parent1.name}', login id: '${parent1.loginId}'.")
tokenLogin(parent1)
dashboardPage.waitForRender()

Log.d(STEP_TAG, "Open the Left Side Navigation Drawer menu.")
dashboardPage.openLeftSideMenu()

Log.d(STEP_TAG, "Open 'Inbox' menu.")
leftSideNavigationDrawerPage.clickInbox()

Log.d(ASSERTION_TAG, "Assert that the conversation is displayed.")
inboxPage.assertConversationDisplayed(seededConversation.subject)

Log.d(STEP_TAG, "Open the conversation.")
inboxPage.openConversation(seededConversation.subject)

Log.d(ASSERTION_TAG, "Assert that the '${conversationSubject}' and '${conversationBody}' are displayed.")
inboxDetailsPage.assertConversationSubject(conversationSubject)
inboxDetailsPage.assertMessageDisplayed(conversationBody)

Log.d(STEP_TAG, "Click Forward button to forward the conversation to ${teacher.name}.")
inboxDetailsPage.pressOverflowMenuItemForConversation("Forward")

val forwardMessage = "Hey, check this out."
Log.d(STEP_TAG, "Type forward message: '$forwardMessage'")
inboxComposeMessagePage.typeBody(forwardMessage)

Log.d(STEP_TAG, "Select recipient for forwarded message.")
inboxComposeMessagePage.pressAddRecipient()

Log.d(STEP_TAG, "Open 'Observers' category to verify only ${parent1.name} is visible and '${parent2.name}' is NOT displayed in Observers list.")
inboxRecipientPickerPage.pressLabel("Observers")
inboxRecipientPickerPage.assertRecipientDisplayed(parent1.shortName)
inboxRecipientPickerPage.assertRecipientNotDisplayed(parent2.shortName)

Log.d(STEP_TAG, "Navigate back from Observers view.")
inboxRecipientPickerPage.pressBack()

Log.d(STEP_TAG, "Select ${teacher.name} from Teachers category.")
inboxRecipientPickerPage.pressLabel("Teachers")
inboxRecipientPickerPage.pressLabel(teacher.shortName)
inboxRecipientPickerPage.pressDone()

Log.d(ASSERTION_TAG, "Assert that send button is enabled after selecting recipient.")
inboxComposeMessagePage.assertIfSendButtonState(true)

Log.d(STEP_TAG, "Send the forwarded message.")
inboxComposeMessagePage.pressSendButton()

Log.d(ASSERTION_TAG, "Assert that the forward message is displayed in the conversation.")
inboxDetailsPage.assertMessageDisplayed(forwardMessage)

Log.d(ASSERTION_TAG, "Assert that the original message is still displayed.")
inboxDetailsPage.assertMessageDisplayed(conversationBody)

Log.d(STEP_TAG, "Navigate back to Inbox conversation list page.")
Espresso.pressBack()

Log.d(ASSERTION_TAG, "Assert that the conversation is still displayed in inbox.")
inboxPage.assertConversationDisplayed(conversationSubject)

Log.d(STEP_TAG, "Navigate back to Dashboard page and open the Left Side Navigation Drawer menu (to be able to log out).")
Espresso.pressBack()
dashboardPage.openLeftSideMenu()

Log.d(STEP_TAG, "Log out from '${parent1.name}' account.")
leftSideNavigationDrawerPage.logout()

Log.d(STEP_TAG, "Login with user: '${parent2.name}', login id: '${parent2.loginId}'.")
tokenLogin(parent2)
dashboardPage.waitForRender()

Log.d(STEP_TAG, "Open Inbox Page.")
dashboardPage.openLeftSideMenu()
leftSideNavigationDrawerPage.clickInbox()

Log.d(ASSERTION_TAG, "Assert that the forwarded conversation is not displayed for '${parent2.name}' as they were not a recipient and we forwarded the message to ${teacher.name}.")
inboxPage.assertConversationNotDisplayed(conversationSubject)
inboxPage.assertInboxEmpty()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,21 @@

package com.instructure.parentapp.ui.espresso

import androidx.work.DefaultWorkerFactory
import androidx.work.Configuration
import androidx.work.WorkerFactory
import com.instructure.canvas.espresso.WorkManagerTestAppManager
import com.instructure.canvas.espresso.WorkManagerTestHelper
import com.instructure.pandautils.features.reminder.AlarmScheduler
import com.instructure.parentapp.util.BaseAppManager

open class TestAppManager : BaseAppManager() {
open class TestAppManager : BaseAppManager(), WorkManagerTestAppManager {

private var workerFactory: WorkerFactory? = null
override val workManagerTestHelper = WorkManagerTestHelper()

override fun getWorkManagerFactory(): WorkerFactory {
return workerFactory ?: DefaultWorkerFactory
}
override val workManagerConfiguration: Configuration
get() = workManagerTestHelper.workManagerConfiguration

override fun getWorkManagerFactory(): WorkerFactory = workManagerTestHelper.getWorkManagerFactory()

override fun getScheduler(): AlarmScheduler? {
return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,34 @@

package com.instructure.parentapp.utils

import android.app.Activity
import android.app.Instrumentation
import android.content.Intent
import android.net.Uri
import androidx.core.content.FileProvider
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.platform.app.InstrumentationRegistry
import com.instructure.canvas.espresso.CanvasTest
import com.instructure.canvas.espresso.common.pages.AboutPage
import com.instructure.canvas.espresso.common.pages.CanvasNetworkSignInPage
import com.instructure.canvas.espresso.common.pages.FileChooserPage
import com.instructure.canvas.espresso.common.pages.InboxPage
import com.instructure.canvas.espresso.common.pages.LegalPage
import com.instructure.canvas.espresso.common.pages.LoginFindSchoolPage
import com.instructure.canvas.espresso.common.pages.LoginLandingPage
import com.instructure.canvas.espresso.common.pages.LoginSignInPage
import com.instructure.canvas.espresso.common.pages.WrongDomainPage
import com.instructure.pandautils.utils.Const
import com.instructure.parentapp.BuildConfig
import com.instructure.parentapp.features.login.LoginActivity
import com.instructure.parentapp.ui.pages.classic.DashboardPage
import com.instructure.parentapp.ui.pages.classic.FrontPagePage
import com.instructure.parentapp.ui.pages.classic.HelpPage
import com.instructure.parentapp.ui.pages.classic.LeftSideNavigationDrawerPage
import com.instructure.parentapp.ui.pages.compose.SyllabusPage
import org.hamcrest.core.AllOf
import java.io.File


abstract class ParentTest : CanvasTest() {
Expand All @@ -47,6 +59,7 @@ abstract class ParentTest : CanvasTest() {
val helpPage = HelpPage()
val syllabusPage = SyllabusPage()
val frontPagePage = FrontPagePage()
val fileChooserPage = FileChooserPage()

// Common pages (it's common for all apps)
val loginLandingPage = LoginLandingPage()
Expand All @@ -57,4 +70,40 @@ abstract class ParentTest : CanvasTest() {
val inboxPage = InboxPage()
val legalPage = LegalPage()
val aboutPage = AboutPage()

fun setupFileOnDevice(fileName: String): Uri {
File(InstrumentationRegistry.getInstrumentation().targetContext.cacheDir, "file_upload").deleteRecursively()
copyAssetFileToExternalCache(activityRule.activity, fileName)

val dir = activityRule.activity.externalCacheDir
val file = File(dir?.path, fileName)

val instrumentationContext = InstrumentationRegistry.getInstrumentation().context
return FileProvider.getUriForFile(
instrumentationContext,
"com.instructure.parentapp" + Const.FILE_PROVIDER_AUTHORITY,
file
)
}

fun stubFilePickerIntent(fileName: String) {
val resultData = Intent()
val dir = activityRule.activity.externalCacheDir
val file = File(dir?.path, fileName)
val newFileUri = FileProvider.getUriForFile(
activityRule.activity,
"com.instructure.parentapp" + Const.FILE_PROVIDER_AUTHORITY,
file
)
resultData.data = newFileUri
resultData.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

Intents.intending(
AllOf.allOf(
IntentMatchers.hasAction(Intent.ACTION_GET_CONTENT),
IntentMatchers.hasType("*/*"),
)
).respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, resultData))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.instructure.canvas.espresso.CanvasTest
import com.instructure.canvas.espresso.common.pages.AboutPage
import com.instructure.canvas.espresso.common.pages.CanvasNetworkSignInPage
import com.instructure.canvas.espresso.common.pages.EmailNotificationsPage
import com.instructure.canvas.espresso.common.pages.FileChooserPage
import com.instructure.canvas.espresso.common.pages.InboxPage
import com.instructure.canvas.espresso.common.pages.LegalPage
import com.instructure.canvas.espresso.common.pages.LoginFindSchoolPage
Expand All @@ -59,7 +60,6 @@ import com.instructure.student.ui.pages.classic.CourseGradesPage
import com.instructure.student.ui.pages.classic.DashboardPage
import com.instructure.student.ui.pages.classic.DiscussionDetailsPage
import com.instructure.student.ui.pages.classic.DiscussionListPage
import com.instructure.student.ui.pages.classic.FileChooserPage
import com.instructure.student.ui.pages.classic.FileListPage
import com.instructure.student.ui.pages.classic.GoToQuizPage
import com.instructure.student.ui.pages.classic.GradesPage
Expand Down
Binary file added apps/teacher/src/androidTest/assets/samplepdf.pdf
Binary file not shown.
Binary file not shown.
Loading
Loading