Skip to content
Merged
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
1 change: 1 addition & 0 deletions apps/student/src/androidTest/assets/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is a test file for assignment submission.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,21 @@
*/
package com.instructure.student.ui.interaction

import android.Manifest
import android.app.Activity
import android.app.Instrumentation
import android.content.Intent
import android.net.Uri
import android.provider.MediaStore
import androidx.compose.ui.platform.ComposeView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiSelector
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils
import com.google.android.apps.common.testing.accessibility.framework.checks.SpeakableTextPresentCheck
import com.instructure.canvas.espresso.FeatureCategory
Expand All @@ -31,6 +44,7 @@ import com.instructure.canvas.espresso.mockcanvas.addAssignmentsToGroups
import com.instructure.canvas.espresso.mockcanvas.addSubmissionForAssignment
import com.instructure.canvas.espresso.mockcanvas.fakes.FakeCustomGradeStatusesManager
import com.instructure.canvas.espresso.mockcanvas.init
import com.instructure.canvas.espresso.refresh
import com.instructure.canvasapi2.di.graphql.CustomGradeStatusModule
import com.instructure.canvasapi2.managers.graphql.CustomGradeStatusesManager
import com.instructure.canvasapi2.models.Assignment
Expand All @@ -49,6 +63,7 @@ import dagger.hilt.android.testing.UninstallModules
import org.hamcrest.Matchers
import org.junit.Assert.assertNotNull
import org.junit.Test
import java.io.File
import java.util.Calendar

@HiltAndroidTest
Expand All @@ -63,9 +78,7 @@ class AssignmentDetailsInteractionTest : StudentComposeTest() {

@Test
@TestMetaData(Priority.MANDATORY, FeatureCategory.SUBMISSIONS, TestCategory.INTERACTION, SecondaryFeatureCategory.SUBMISSIONS_ONLINE_URL)
fun testSubmission_submitAssignment() {
// TODO - Test submitting for each submission type
// For now, I'm going to just test one submission type
fun testSubmission_submitOnlineURL() {
val data = MockCanvas.init(
studentCount = 1,
courseCount = 1
Expand All @@ -89,6 +102,246 @@ class AssignmentDetailsInteractionTest : StudentComposeTest() {
assignmentDetailsPage.assertStatusSubmitted()
}

@Test
@TestMetaData(Priority.MANDATORY, FeatureCategory.SUBMISSIONS, TestCategory.INTERACTION, SecondaryFeatureCategory.SUBMISSIONS_TEXT_ENTRY)
fun testSubmission_submitTextEntry() {
val data = MockCanvas.init(
studentCount = 1,
courseCount = 1
)

val course = data.courses.values.first()
val student = data.students[0]
val token = data.tokenFor(student)!!
val assignment = data.addAssignment(courseId = course.id, submissionTypeList = listOf(Assignment.SubmissionType.ONLINE_TEXT_ENTRY))
data.addSubmissionForAssignment(
assignmentId = assignment.id,
userId = data.users.values.first().id,
type = Assignment.SubmissionType.ONLINE_TEXT_ENTRY.apiString
)
tokenLogin(data.domain, token, student)
routeTo("courses/${course.id}/assignments", data.domain)

assignmentListPage.clickAssignment(assignment)
assignmentDetailsPage.clickSubmit()
textSubmissionUploadPage.typeText("This is my test submission text.")
textSubmissionUploadPage.clickOnSubmitButton()
assignmentDetailsPage.assertStatusSubmitted()
}

@Test
@TestMetaData(Priority.MANDATORY, FeatureCategory.SUBMISSIONS, TestCategory.INTERACTION, SecondaryFeatureCategory.SUBMISSIONS_FILE_UPLOAD)
fun testSubmission_submitFileUpload() {
val data = MockCanvas.init(
studentCount = 1,
courseCount = 1
)

val course = data.courses.values.first()
val student = data.students[0]
val token = data.tokenFor(student)!!
val assignment = data.addAssignment(courseId = course.id, submissionTypeList = listOf(Assignment.SubmissionType.ONLINE_UPLOAD))
data.addSubmissionForAssignment(
assignmentId = assignment.id,
userId = data.users.values.first().id,
type = Assignment.SubmissionType.ONLINE_UPLOAD.apiString
)
tokenLogin(data.domain, token, student)
routeTo("courses/${course.id}/assignments", data.domain)

val fileName = "test.txt"
Intents.init()
try {
stubFilePickerIntent(fileName)
setupFileOnDevice(fileName)

assignmentListPage.clickAssignment(assignment)
assignmentDetailsPage.clickSubmit()
pickerSubmissionUploadPage.chooseDevice()
pickerSubmissionUploadPage.waitForSubmitButtonToAppear()
pickerSubmissionUploadPage.assertFileDisplayed(fileName)
pickerSubmissionUploadPage.submit()
assignmentDetailsPage.assertStatusSubmitted()
} finally {
Intents.release()
}
}

@Test
@TestMetaData(Priority.MANDATORY, FeatureCategory.SUBMISSIONS, TestCategory.INTERACTION, SecondaryFeatureCategory.SUBMISSIONS_MEDIA_RECORDING)
fun testSubmission_submitMediaRecordingChooseMediaFile() {
val data = MockCanvas.init(
studentCount = 1,
courseCount = 1
)

val course = data.courses.values.first()
val student = data.students[0]
val token = data.tokenFor(student)!!
val assignment = data.addAssignment(courseId = course.id, submissionTypeList = listOf(Assignment.SubmissionType.MEDIA_RECORDING))
data.addSubmissionForAssignment(
assignmentId = assignment.id,
userId = data.users.values.first().id,
type = Assignment.SubmissionType.MEDIA_RECORDING.apiString
)
tokenLogin(data.domain, token, student)
routeTo("courses/${course.id}/assignments", data.domain)

val activity = activityRule.activity
grantPermissions(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)

val fileName = "test_video.mp4"
copyAssetFileToExternalCache(activity, fileName)

val resultData = Intent()
val dir = activity.externalCacheDir
val file = File(dir?.path, fileName)
val uri = Uri.fromFile(file)
resultData.data = uri
val activityResult = Instrumentation.ActivityResult(Activity.RESULT_OK, resultData)

Intents.init()
try {
Intents.intending(
Matchers.anyOf(
IntentMatchers.hasAction(Intent.ACTION_GET_CONTENT),
IntentMatchers.hasAction(Intent.ACTION_PICK),
IntentMatchers.hasAction(Intent.ACTION_OPEN_DOCUMENT)
)
).respondWith(activityResult)

assignmentListPage.clickAssignment(assignment)
assignmentDetailsPage.clickSubmit()

onView(withId(R.id.submissionEntryMediaFile)).perform(click())

pickerSubmissionUploadPage.waitForSubmitButtonToAppear()
pickerSubmissionUploadPage.assertFileDisplayed(fileName)
pickerSubmissionUploadPage.submit()
assignmentDetailsPage.assertStatusSubmitted()
} finally {
Intents.release()
}
}

@Test
@TestMetaData(Priority.MANDATORY, FeatureCategory.SUBMISSIONS, TestCategory.INTERACTION, SecondaryFeatureCategory.SUBMISSIONS_MEDIA_RECORDING)
fun testSubmission_submitMediaRecordingRecordVideo() {
val data = MockCanvas.init(
studentCount = 1,
courseCount = 1
)

val course = data.courses.values.first()
val student = data.students[0]
val token = data.tokenFor(student)!!
val assignment = data.addAssignment(courseId = course.id, submissionTypeList = listOf(Assignment.SubmissionType.MEDIA_RECORDING))
data.addSubmissionForAssignment(
assignmentId = assignment.id,
userId = data.users.values.first().id,
type = Assignment.SubmissionType.MEDIA_RECORDING.apiString
)
tokenLogin(data.domain, token, student)
routeTo("courses/${course.id}/assignments", data.domain)

val activity = activityRule.activity
grantPermissions(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)

val testVideoFile = "test_video.mp4"
copyAssetFileToExternalCache(activity, testVideoFile)

var capturedVideoUri: Uri? = null

Intents.init()
Intents.intending(
Matchers.allOf(
IntentMatchers.hasAction(MediaStore.ACTION_VIDEO_CAPTURE),
IntentMatchers.hasExtraWithKey(MediaStore.EXTRA_OUTPUT)
)
).respondWithFunction { intent ->
val outputUri = intent.extras?.get(MediaStore.EXTRA_OUTPUT) as? Uri
capturedVideoUri = outputUri
if (outputUri != null) {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val dir = context.externalCacheDir
val sampleFile = File(dir, testVideoFile)
if (outputUri.scheme == "file") {
val destFile = File(outputUri.path!!)
destFile.parentFile?.mkdirs()
sampleFile.copyTo(destFile, overwrite = true)
} else if (outputUri.scheme == "content") {
context.contentResolver.openOutputStream(outputUri)?.use { outputStream ->
sampleFile.inputStream().use { inputStream ->
inputStream.copyTo(outputStream)
}
}
}
}
Instrumentation.ActivityResult(Activity.RESULT_OK, Intent())
}

assignmentListPage.clickAssignment(assignment)
assignmentDetailsPage.clickSubmit()
onView(withId(R.id.submissionEntryVideo)).perform(click())

Intents.release()

pickerSubmissionUploadPage.waitForSubmitButtonToAppear()

val fileName = File(capturedVideoUri!!.path!!).name
pickerSubmissionUploadPage.assertFileDisplayed(fileName)
pickerSubmissionUploadPage.submit()

assignmentDetailsPage.assertStatusSubmitted()
}

@Test
@TestMetaData(Priority.MANDATORY, FeatureCategory.SUBMISSIONS, TestCategory.INTERACTION, SecondaryFeatureCategory.SUBMISSIONS_MEDIA_RECORDING)
fun testSubmission_submitMediaRecordingRecordAudio() {
val data = MockCanvas.init(
studentCount = 1,
courseCount = 1
)

val course = data.courses.values.first()
val student = data.students[0]
val token = data.tokenFor(student)!!
val assignment = data.addAssignment(courseId = course.id, submissionTypeList = listOf(Assignment.SubmissionType.MEDIA_RECORDING))
data.addSubmissionForAssignment(
assignmentId = assignment.id,
userId = data.users.values.first().id,
type = Assignment.SubmissionType.MEDIA_RECORDING.apiString
)
tokenLogin(data.domain, token, student)
routeTo("courses/${course.id}/assignments", data.domain)

val activity = activityRule.activity
grantPermissions(Manifest.permission.RECORD_AUDIO)

val testAudioFileName = "test_audio.mp3"
copyAssetFileToExternalCache(activity, testAudioFileName)

val context = InstrumentationRegistry.getInstrumentation().targetContext
val recordingFile = File(context.externalCacheDir, "audio.amr")
val testAudioFile = File(context.externalCacheDir, testAudioFileName)
testAudioFile.copyTo(recordingFile, overwrite = true)

assignmentListPage.clickAssignment(assignment)
assignmentDetailsPage.clickSubmit()
onView(withId(R.id.submissionEntryAudio)).perform(click())

device.findObject(UiSelector().resourceIdMatches(".*recordAudioButton")).click()

testAudioFile.copyTo(recordingFile, overwrite = true)

device.findObject(UiSelector().resourceIdMatches(".*stopButton")).click()

device.findObject(UiSelector().resourceIdMatches(".*sendAudioButton")).click()

refresh()
assignmentDetailsPage.assertStatusSubmitted()
}

@Test
@TestMetaData(Priority.MANDATORY, FeatureCategory.ASSIGNMENTS, TestCategory.INTERACTION)
fun testSubmissionStatus_NotSubmitted() {
Expand Down Expand Up @@ -694,6 +947,16 @@ class AssignmentDetailsInteractionTest : StudentComposeTest() {
return assignment
}

private fun grantPermissions(vararg permissions: String) {
val activity = activityRule.activity
permissions.forEach { permission ->
InstrumentationRegistry.getInstrumentation().uiAutomation.grantRuntimePermission(
activity.packageName,
permission
)
}
}

override fun enableAndConfigureAccessibilityChecks() {
extraAccessibilitySupressions = Matchers.allOf(
AccessibilityCheckResultUtils.matchesCheck(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ enum class FeatureCategory {

enum class SecondaryFeatureCategory {
NONE, LOGIN_K5,
SUBMISSIONS_TEXT_ENTRY, SUBMISSIONS_ANNOTATIONS, SUBMISSIONS_ONLINE_URL, SUBMISSIONS_MULTIPLE_TYPE,
SUBMISSIONS_TEXT_ENTRY, SUBMISSIONS_ANNOTATIONS, SUBMISSIONS_ONLINE_URL, SUBMISSIONS_MULTIPLE_TYPE, SUBMISSIONS_FILE_UPLOAD, SUBMISSIONS_MEDIA_RECORDING,
ASSIGNMENT_COMMENTS, ASSIGNMENT_QUIZZES, ASSIGNMENT_DISCUSSIONS, HOMEROOM, K5_GRADES, IMPORTANT_DATES, RESOURCES, SCHEDULE,
GROUPS_DASHBOARD, GROUPS_FILES, GROUPS_ANNOUNCEMENTS, GROUPS_DISCUSSIONS, GROUPS_PAGES, GROUPS_PEOPLE,
EVENTS_DISCUSSIONS, EVENTS_QUIZZES, EVENTS_ASSIGNMENTS, EVENTS_NOTIFICATIONS, SETTINGS_EMAIL_NOTIFICATIONS,
Expand Down
Loading