Skip to content

Commit 406f751

Browse files
Merge branch 'master' into MBL-19640-grades-and-what-if-tests
# Conflicts: # apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentTest.kt
2 parents 3efbb1b + 68b5f92 commit 406f751

File tree

348 files changed

+27434
-1319
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

348 files changed

+27434
-1319
lines changed

apps/buildSrc/src/main/java/GlobalDependencies.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ object Versions {
4646
const val JAVA_JWT = "4.5.0"
4747
const val GLANCE = "1.1.1"
4848
const val LIVEDATA = "1.9.0"
49+
const val REORDERABLE = "2.4.0"
4950
}
5051

5152
object Libs {
@@ -195,6 +196,7 @@ object Libs {
195196
const val COMPOSE_MATERIAL3_WINDOW_SIZE = "androidx.compose.material3:material3-window-size-class"
196197
const val COMPOSE_NAVIGATION_HILT = "androidx.hilt:hilt-navigation-compose:1.3.0"
197198
const val COMPOSE_FRAGMENT = "androidx.fragment:fragment-compose:1.8.9"
199+
const val COMPOSE_REORDERABLE = "sh.calvin.reorderable:reorderable:${Versions.REORDERABLE}"
198200

199201
// Glance
200202
const val GLANCE = "androidx.glance:glance:${Versions.GLANCE}"

apps/parent/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ android {
4141
applicationId "com.instructure.parentapp"
4242
minSdkVersion Versions.MIN_SDK
4343
targetSdkVersion Versions.TARGET_SDK
44-
versionCode 64
45-
versionName "4.8.0"
44+
versionCode 65
45+
versionName "4.9.0"
4646

4747
buildConfigField "boolean", "IS_TESTING", "false"
4848
testInstrumentationRunner 'com.instructure.parentapp.ui.espresso.ParentHiltTestRunner'

apps/parent/flank_e2e.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ gcloud:
1313
timeout: 60m
1414
test-targets:
1515
- annotation com.instructure.canvas.espresso.annotations.E2E
16-
- notAnnotation com.instructure.canvas.espresso.annotations.Stub, com.instructure.canvas.espresso.annotations.FlakyE2E, com.instructure.canvas.espresso.annotations.KnownBug, com.instructure.canvas.espresso.annotations.OfflineE2E
16+
- notAnnotation com.instructure.canvas.espresso.annotations.Stub, com.instructure.canvas.espresso.annotations.FlakyE2E, com.instructure.canvas.espresso.annotations.KnownBug, com.instructure.canvas.espresso.annotations.OfflineE2E, com.instructure.canvas.espresso.annotations.ReleaseExclude
1717
device:
1818
- model: Pixel2.arm
1919
version: 29
@@ -23,4 +23,3 @@ gcloud:
2323
flank:
2424
testShards: 10
2525
testRuns: 1
26-
5.25 MB
Binary file not shown.

apps/parent/src/androidTest/java/com/instructure/parentapp/ui/e2e/compose/InboxE2ETest.kt

Lines changed: 219 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,22 @@
1414
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1515
*
1616
*/
17+
1718
package com.instructure.parentapp.ui.e2e.compose
1819

1920
import android.os.SystemClock.sleep
2021
import android.util.Log
2122
import androidx.test.espresso.Espresso
23+
import androidx.test.espresso.intent.Intents
2224
import androidx.test.espresso.matcher.ViewMatchers
25+
import androidx.test.platform.app.InstrumentationRegistry
26+
import androidx.test.uiautomator.UiDevice
27+
import androidx.test.uiautomator.UiSelector
2328
import com.instructure.canvas.espresso.FeatureCategory
2429
import com.instructure.canvas.espresso.Priority
2530
import com.instructure.canvas.espresso.TestCategory
2631
import com.instructure.canvas.espresso.TestMetaData
2732
import com.instructure.canvas.espresso.annotations.E2E
28-
import com.instructure.canvas.espresso.annotations.ReleaseExclude
2933
import com.instructure.canvas.espresso.refresh
3034
import com.instructure.canvasapi2.models.CanvasContext
3135
import com.instructure.canvasapi2.utils.toApiString
@@ -42,6 +46,7 @@ import com.instructure.parentapp.utils.extensions.seedData
4246
import com.instructure.parentapp.utils.extensions.tokenLogin
4347
import dagger.hilt.android.testing.HiltAndroidTest
4448
import org.junit.Test
49+
import java.io.File
4550
import java.util.Date
4651

4752
@HiltAndroidTest
@@ -54,7 +59,6 @@ class InboxE2ETest: ParentComposeTest() {
5459
@E2E
5560
@Test
5661
@TestMetaData(Priority.MANDATORY, FeatureCategory.INBOX, TestCategory.E2E)
57-
@ReleaseExclude
5862
fun testInboxSelectedButtonActionsE2E() {
5963

6064
Log.d(PREPARATION_TAG, "Seeding data.")
@@ -619,4 +623,217 @@ class InboxE2ETest: ParentComposeTest() {
619623
inboxPage.openConversation(expectedSubjectEvent)
620624
inboxDetailsPage.assertMessageDisplayed(expectedMessage)
621625
}
626+
627+
@E2E
628+
@Test
629+
@TestMetaData(Priority.IMPORTANT, FeatureCategory.INBOX, TestCategory.E2E)
630+
fun testInboxMessageReplyWithVideoAttachmentE2E() {
631+
632+
Log.d(PREPARATION_TAG, "Seeding data.")
633+
val data = seedData(students = 1, teachers = 1, parents = 1, courses = 1)
634+
val parent = data.parentsList[0]
635+
val teacher = data.teachersList[0]
636+
val course = data.coursesList[0]
637+
638+
Log.d(PREPARATION_TAG, "Copy mp4 file to Downloads folder for attachment.")
639+
val videoFileName = "test_video.mp4"
640+
setupFileOnDevice(videoFileName)
641+
File(InstrumentationRegistry.getInstrumentation().targetContext.cacheDir, "file_upload").deleteRecursively()
642+
643+
val conversationSubject = "Need Document Help"
644+
val conversationBody = "Can you please send me the course document?"
645+
Log.d(PREPARATION_TAG, "Create a conversation from '${teacher.name}' to '${parent.name}'.")
646+
val seededConversation = ConversationsApi.createConversationForCourse(token = teacher.token, courseId = course.id, recipients = listOf(parent.id.toString()), subject = conversationSubject, body = conversationBody)[0]
647+
648+
Log.d(STEP_TAG, "Login with user: '${parent.name}', login id: '${parent.loginId}'.")
649+
tokenLogin(parent)
650+
dashboardPage.waitForRender()
651+
652+
Log.d(STEP_TAG, "Open the Left Side Navigation Drawer menu.")
653+
dashboardPage.openLeftSideMenu()
654+
655+
Log.d(STEP_TAG, "Open 'Inbox' menu.")
656+
leftSideNavigationDrawerPage.clickInbox()
657+
658+
Log.d(ASSERTION_TAG, "Assert that the conversation is displayed.")
659+
inboxPage.assertConversationDisplayed(seededConversation.subject)
660+
661+
Log.d(STEP_TAG, "Open the conversation.")
662+
inboxPage.openConversation(seededConversation.subject)
663+
664+
Log.d(ASSERTION_TAG, "Assert that the '${conversationSubject}' and '${conversationBody}' are displayed.")
665+
inboxDetailsPage.assertConversationSubject(conversationSubject)
666+
inboxDetailsPage.assertMessageDisplayed(conversationBody)
667+
668+
Log.d(STEP_TAG, "Click Reply button to respond to the conversation.")
669+
inboxDetailsPage.pressOverflowMenuItemForConversation("Reply")
670+
671+
val replyMessage = "Sure! Here is the document."
672+
Log.d(STEP_TAG, "Type reply message: '$replyMessage'")
673+
inboxComposeMessagePage.typeBody(replyMessage)
674+
675+
Log.d(ASSERTION_TAG, "Assert that send button is enabled after typing message.")
676+
inboxComposeMessagePage.assertIfSendButtonState(true)
677+
678+
Log.d(STEP_TAG, "Click attachment button to open file picker dialog.")
679+
inboxComposeMessagePage.clickAttachmentButton()
680+
681+
Log.d(PREPARATION_TAG, "Simulate file picker intent (again).")
682+
Intents.init()
683+
try {
684+
stubFilePickerIntent(videoFileName)
685+
fileChooserPage.chooseDevice()
686+
}
687+
finally {
688+
Intents.release()
689+
}
690+
691+
Log.d(STEP_TAG, "Click OKAY button to confirm file selection.")
692+
fileChooserPage.clickOkay()
693+
694+
Log.d(ASSERTION_TAG, "Assert that the video file is displayed as attached in the screen.")
695+
inboxComposeMessagePage.assertAttachmentDisplayed(videoFileName)
696+
697+
Log.d(STEP_TAG, "Send the reply message with attachment.")
698+
sleep(2000) //Wait for attachment to finish uploading
699+
inboxComposeMessagePage.pressSendButton()
700+
701+
Log.d(ASSERTION_TAG, "Assert that the reply message, attachment, and original message are displayed in the conversation.")
702+
inboxDetailsPage.assertMessageDisplayed(replyMessage)
703+
inboxDetailsPage.assertAttachmentDisplayed(videoFileName)
704+
inboxDetailsPage.assertMessageDisplayed(conversationBody)
705+
706+
Log.d(STEP_TAG, "Click on the video attachment to download it.")
707+
inboxDetailsPage.clickAttachment(videoFileName)
708+
709+
Log.d(STEP_TAG, "Wait for download to complete.")
710+
sleep(5000)
711+
712+
Log.d(STEP_TAG, "Open the Notification bar.")
713+
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
714+
device.openNotification()
715+
716+
retryWithIncreasingDelay(times = 10, maxDelay = 3000) {
717+
Log.d(STEP_TAG, "Find download notification.")
718+
val downloadNotification = device.findObject(UiSelector().textContains(videoFileName).className("android.widget.TextView"))
719+
720+
Log.d(ASSERTION_TAG, "Assert that 'Download complete' text is displayed in notification.")
721+
val downloadCompleteText = device.findObject(UiSelector().textContains("Download complete"))
722+
assert(downloadCompleteText.exists()) { "Download complete text not found in notification" }
723+
724+
Log.d(ASSERTION_TAG, "Assert that file name '$videoFileName' is displayed in notification.")
725+
assert(downloadNotification.exists()) { "File name '$videoFileName' not found in notification" }
726+
}
727+
728+
Log.d(STEP_TAG, "Close notification shade.")
729+
device.pressBack()
730+
731+
Log.d(STEP_TAG, "Assert that the '${conversationSubject}' is displayed.")
732+
inboxDetailsPage.assertConversationSubject(conversationSubject)
733+
734+
Log.d(STEP_TAG, "Navigate back to inbox.")
735+
Espresso.pressBack()
736+
737+
Log.d(ASSERTION_TAG, "Assert that the conversation is still displayed in inbox.")
738+
inboxPage.assertConversationDisplayed(seededConversation.subject)
739+
}
740+
741+
@E2E
742+
@Test
743+
@TestMetaData(Priority.IMPORTANT, FeatureCategory.INBOX, TestCategory.E2E)
744+
fun testInboxMessageForwardE2E() {
745+
746+
Log.d(PREPARATION_TAG, "Seeding data.")
747+
val data = seedData(students = 1, teachers = 1, parents = 2, courses = 1)
748+
val parent1 = data.parentsList[0]
749+
val parent2 = data.parentsList[1]
750+
val teacher = data.teachersList[0]
751+
val course = data.coursesList[0]
752+
753+
val conversationSubject = "Important Announcement"
754+
val conversationBody = "Please review this."
755+
Log.d(PREPARATION_TAG, "Create a conversation from '${teacher.name}' to '${parent1.name}'.")
756+
val seededConversation = ConversationsApi.createConversationForCourse(token = teacher.token, courseId = course.id, recipients = listOf(parent1.id.toString()), subject = conversationSubject, body = conversationBody)[0]
757+
758+
Log.d(STEP_TAG, "Login with user: '${parent1.name}', login id: '${parent1.loginId}'.")
759+
tokenLogin(parent1)
760+
dashboardPage.waitForRender()
761+
762+
Log.d(STEP_TAG, "Open the Left Side Navigation Drawer menu.")
763+
dashboardPage.openLeftSideMenu()
764+
765+
Log.d(STEP_TAG, "Open 'Inbox' menu.")
766+
leftSideNavigationDrawerPage.clickInbox()
767+
768+
Log.d(ASSERTION_TAG, "Assert that the conversation is displayed.")
769+
inboxPage.assertConversationDisplayed(seededConversation.subject)
770+
771+
Log.d(STEP_TAG, "Open the conversation.")
772+
inboxPage.openConversation(seededConversation.subject)
773+
774+
Log.d(ASSERTION_TAG, "Assert that the '${conversationSubject}' and '${conversationBody}' are displayed.")
775+
inboxDetailsPage.assertConversationSubject(conversationSubject)
776+
inboxDetailsPage.assertMessageDisplayed(conversationBody)
777+
778+
Log.d(STEP_TAG, "Click Forward button to forward the conversation to ${teacher.name}.")
779+
inboxDetailsPage.pressOverflowMenuItemForConversation("Forward")
780+
781+
val forwardMessage = "Hey, check this out."
782+
Log.d(STEP_TAG, "Type forward message: '$forwardMessage'")
783+
inboxComposeMessagePage.typeBody(forwardMessage)
784+
785+
Log.d(STEP_TAG, "Select recipient for forwarded message.")
786+
inboxComposeMessagePage.pressAddRecipient()
787+
788+
Log.d(STEP_TAG, "Open 'Observers' category to verify only ${parent1.name} is visible and '${parent2.name}' is NOT displayed in Observers list.")
789+
inboxRecipientPickerPage.pressLabel("Observers")
790+
inboxRecipientPickerPage.assertRecipientDisplayed(parent1.shortName)
791+
inboxRecipientPickerPage.assertRecipientNotDisplayed(parent2.shortName)
792+
793+
Log.d(STEP_TAG, "Navigate back from Observers view.")
794+
inboxRecipientPickerPage.pressBack()
795+
796+
Log.d(STEP_TAG, "Select ${teacher.name} from Teachers category.")
797+
inboxRecipientPickerPage.pressLabel("Teachers")
798+
inboxRecipientPickerPage.pressLabel(teacher.shortName)
799+
inboxRecipientPickerPage.pressDone()
800+
801+
Log.d(ASSERTION_TAG, "Assert that send button is enabled after selecting recipient.")
802+
inboxComposeMessagePage.assertIfSendButtonState(true)
803+
804+
Log.d(STEP_TAG, "Send the forwarded message.")
805+
inboxComposeMessagePage.pressSendButton()
806+
807+
Log.d(ASSERTION_TAG, "Assert that the forward message is displayed in the conversation.")
808+
inboxDetailsPage.assertMessageDisplayed(forwardMessage)
809+
810+
Log.d(ASSERTION_TAG, "Assert that the original message is still displayed.")
811+
inboxDetailsPage.assertMessageDisplayed(conversationBody)
812+
813+
Log.d(STEP_TAG, "Navigate back to Inbox conversation list page.")
814+
Espresso.pressBack()
815+
816+
Log.d(ASSERTION_TAG, "Assert that the conversation is still displayed in inbox.")
817+
inboxPage.assertConversationDisplayed(conversationSubject)
818+
819+
Log.d(STEP_TAG, "Navigate back to Dashboard page and open the Left Side Navigation Drawer menu (to be able to log out).")
820+
Espresso.pressBack()
821+
dashboardPage.openLeftSideMenu()
822+
823+
Log.d(STEP_TAG, "Log out from '${parent1.name}' account.")
824+
leftSideNavigationDrawerPage.logout()
825+
826+
Log.d(STEP_TAG, "Login with user: '${parent2.name}', login id: '${parent2.loginId}'.")
827+
tokenLogin(parent2)
828+
dashboardPage.waitForRender()
829+
830+
Log.d(STEP_TAG, "Open Inbox Page.")
831+
dashboardPage.openLeftSideMenu()
832+
leftSideNavigationDrawerPage.clickInbox()
833+
834+
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}.")
835+
inboxPage.assertConversationNotDisplayed(conversationSubject)
836+
inboxPage.assertInboxEmpty()
837+
}
838+
622839
}

apps/parent/src/androidTest/java/com/instructure/parentapp/ui/espresso/TestAppManager.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,21 @@
1717

1818
package com.instructure.parentapp.ui.espresso
1919

20-
import androidx.work.DefaultWorkerFactory
20+
import androidx.work.Configuration
2121
import androidx.work.WorkerFactory
22+
import com.instructure.canvas.espresso.WorkManagerTestAppManager
23+
import com.instructure.canvas.espresso.WorkManagerTestHelper
2224
import com.instructure.pandautils.features.reminder.AlarmScheduler
2325
import com.instructure.parentapp.util.BaseAppManager
2426

25-
open class TestAppManager : BaseAppManager() {
27+
open class TestAppManager : BaseAppManager(), WorkManagerTestAppManager {
2628

27-
private var workerFactory: WorkerFactory? = null
29+
override val workManagerTestHelper = WorkManagerTestHelper()
2830

29-
override fun getWorkManagerFactory(): WorkerFactory {
30-
return workerFactory ?: DefaultWorkerFactory
31-
}
31+
override val workManagerConfiguration: Configuration
32+
get() = workManagerTestHelper.workManagerConfiguration
33+
34+
override fun getWorkManagerFactory(): WorkerFactory = workManagerTestHelper.getWorkManagerFactory()
3235

3336
override fun getScheduler(): AlarmScheduler? {
3437
return null

apps/parent/src/androidTest/java/com/instructure/parentapp/ui/interaction/AssignmentDetailsInteractionTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ class AssignmentDetailsInteractionTest : ParentComposeTest() {
246246
val assignment = addAssignment(MockCanvas.data, Assignment.GradingType.GPA_SCALE, "3.7", 90.0, 100)
247247
gotoAssignment(data, assignment)
248248

249-
assignmentDetailsPage.assertGradeDisplayed("3.7")
249+
assignmentDetailsPage.assertGradeDisplayed("A")
250250
assignmentDetailsPage.assertOutOfTextNotDisplayed()
251251
assignmentDetailsPage.assertScoreNotDisplayed()
252252
}

apps/parent/src/androidTest/java/com/instructure/parentapp/ui/interaction/ParentInboxComposeInteractionTest.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.instructure.canvas.espresso.mockcanvas.fakes.FakeAssignmentDetailsMan
1414
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeCommentLibraryManager
1515
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeInboxSettingsManager
1616
import com.instructure.canvas.espresso.mockcanvas.fakes.FakePostPolicyManager
17+
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeRecentGradedSubmissionsManager
1718
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeSubmissionCommentsManager
1819
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeSubmissionContentManager
1920
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeSubmissionDetailsManager
@@ -26,6 +27,7 @@ import com.instructure.canvasapi2.managers.InboxSettingsManager
2627
import com.instructure.canvasapi2.managers.PostPolicyManager
2728
import com.instructure.canvasapi2.managers.SubmissionRubricManager
2829
import com.instructure.canvasapi2.managers.graphql.AssignmentDetailsManager
30+
import com.instructure.canvasapi2.managers.graphql.RecentGradedSubmissionsManager
2931
import com.instructure.canvasapi2.managers.graphql.SubmissionCommentsManager
3032
import com.instructure.canvasapi2.managers.graphql.SubmissionContentManager
3133
import com.instructure.canvasapi2.managers.graphql.SubmissionDetailsManager
@@ -96,6 +98,10 @@ class ParentInboxComposeInteractionTest: InboxComposeInteractionTest() {
9698
@JvmField
9799
val postPolicyManager: PostPolicyManager = FakePostPolicyManager()
98100

101+
@BindValue
102+
@JvmField
103+
val recentGradedSubmissionsManager: RecentGradedSubmissionsManager = FakeRecentGradedSubmissionsManager()
104+
99105
@Test
100106
fun testParentComposeDefaultValues() {
101107
val data = initData(canSendToAll = true)

apps/parent/src/androidTest/java/com/instructure/parentapp/ui/interaction/ParentInboxSignatureInteractionTest.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import com.instructure.canvas.espresso.mockcanvas.fakes.FakeAssignmentDetailsMan
2525
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeCommentLibraryManager
2626
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeInboxSettingsManager
2727
import com.instructure.canvas.espresso.mockcanvas.fakes.FakePostPolicyManager
28+
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeRecentGradedSubmissionsManager
2829
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeSubmissionCommentsManager
2930
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeSubmissionContentManager
3031
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeSubmissionDetailsManager
@@ -37,6 +38,7 @@ import com.instructure.canvasapi2.managers.InboxSettingsManager
3738
import com.instructure.canvasapi2.managers.PostPolicyManager
3839
import com.instructure.canvasapi2.managers.SubmissionRubricManager
3940
import com.instructure.canvasapi2.managers.graphql.AssignmentDetailsManager
41+
import com.instructure.canvasapi2.managers.graphql.RecentGradedSubmissionsManager
4042
import com.instructure.canvasapi2.managers.graphql.SubmissionCommentsManager
4143
import com.instructure.canvasapi2.managers.graphql.SubmissionContentManager
4244
import com.instructure.canvasapi2.managers.graphql.SubmissionDetailsManager
@@ -92,6 +94,10 @@ class ParentInboxSignatureInteractionTest : InboxSignatureInteractionTest() {
9294
@JvmField
9395
val postPolicyManager: PostPolicyManager = FakePostPolicyManager()
9496

97+
@BindValue
98+
@JvmField
99+
val recentGradedSubmissionsManager: RecentGradedSubmissionsManager = FakeRecentGradedSubmissionsManager()
100+
95101
private val dashboardPage = DashboardPage()
96102
private val leftSideNavigationDrawerPage = LeftSideNavigationDrawerPage()
97103

0 commit comments

Comments
 (0)