Skip to content

Commit

Permalink
[Student][E2E][MBL-12978] | E2E tests for assignments (#234)
Browse files Browse the repository at this point in the history
* MBL-12978: Initial effort towards AssignmentsE2ETest

* MBL-12978: More logic for AssignmentsE2E test

* MBL-12978: More tweaks

* MBL-12978: Addressed PR feedback

Removed commented-out println
  • Loading branch information
joehoag authored Aug 14, 2019
1 parent cefb3fb commit d1f1af8
Show file tree
Hide file tree
Showing 9 changed files with 347 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -1,24 +1,192 @@
package com.instructure.student.ui.e2e

import android.os.SystemClock.sleep
import androidx.test.espresso.Espresso
import com.instructure.canvas.espresso.E2E
import com.instructure.canvas.espresso.Stub
import com.instructure.dataseeding.api.AssignmentsApi
import com.instructure.dataseeding.api.SubmissionsApi
import com.instructure.dataseeding.model.FileUploadType
import com.instructure.dataseeding.model.GradingType
import com.instructure.dataseeding.model.SubmissionType
import com.instructure.dataseeding.util.days
import com.instructure.dataseeding.util.fromNow
import com.instructure.dataseeding.util.iso8601
import com.instructure.panda_annotations.FeatureCategory
import com.instructure.panda_annotations.Priority
import com.instructure.panda_annotations.TestCategory
import com.instructure.panda_annotations.TestMetaData
import com.instructure.student.ui.utils.StudentTest
import com.instructure.student.ui.utils.seedData
import com.instructure.student.ui.utils.tokenLogin
import com.instructure.student.ui.utils.uploadTextFile
import org.junit.Test

class AssignmentsE2ETest: StudentTest() {
override fun displaysPageObjects() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

@Stub
@E2E
@Test
@TestMetaData(Priority.P0, FeatureCategory.ASSIGNMENTS, TestCategory.E2E, true)
@TestMetaData(Priority.P0, FeatureCategory.ASSIGNMENTS, TestCategory.E2E)
fun testAssignmentsE2E() {
// Seed basic student/teacher/course data
val data = seedData(students = 1, teachers = 1, courses = 1)
val student = data.studentsList[0]
val teacher = data.teachersList[0]
val course = data.coursesList[0]

// Seed some assignments
val pointsTextAssignment = AssignmentsApi.createAssignment(AssignmentsApi.CreateAssignmentRequest(
courseId = course.id,
submissionTypes = listOf(SubmissionType.ONLINE_TEXT_ENTRY),
gradingType = GradingType.POINTS,
teacherToken = teacher.token,
pointsPossible = 15.0,
dueAt = 1.days.fromNow.iso8601
))

val letterGradeTextAssignment = AssignmentsApi.createAssignment(AssignmentsApi.CreateAssignmentRequest(
courseId = course.id,
submissionTypes = listOf(SubmissionType.ONLINE_TEXT_ENTRY),
gradingType = GradingType.LETTER_GRADE,
teacherToken = teacher.token,
pointsPossible = 20.0
))

val percentageFileAssignment = AssignmentsApi.createAssignment(AssignmentsApi.CreateAssignmentRequest(
courseId = course.id,
submissionTypes = listOf(SubmissionType.ONLINE_UPLOAD),
gradingType = GradingType.PERCENT,
teacherToken = teacher.token,
pointsPossible = 25.0,
allowedExtensions = listOf("txt", "pdf", "jpg")
))

// Pre-seed a submission and a grade for the letter grade assignment
SubmissionsApi.seedAssignmentSubmission(SubmissionsApi.SubmissionSeedRequest(
assignmentId = letterGradeTextAssignment.id,
courseId = course.id,
studentToken = student.token,
submissionSeedsList = listOf(SubmissionsApi.SubmissionSeedInfo(
amount = 1,
submissionType = SubmissionType.ONLINE_TEXT_ENTRY
))
))

val submissionGrade = SubmissionsApi.gradeSubmission(
teacherToken = teacher.token,
courseId = course.id,
assignmentId = letterGradeTextAssignment.id,
studentId = student.id,
postedGrade = "16",
excused = false
)


// Sign in with lone student
tokenLogin(student)

// Go into our course
dashboardPage.waitForRender()
dashboardPage.selectCourse(course)

// Select the assignments tab
courseBrowserPage.selectAssignments()

// Verify that our assignments are present, along with any grade/date info
assignmentListPage.assertHasAssignment(pointsTextAssignment)
assignmentListPage.assertHasAssignment(letterGradeTextAssignment, submissionGrade.grade)
assignmentListPage.assertHasAssignment(percentageFileAssignment)

// Let's submit a text assignment
assignmentListPage.clickAssignment(pointsTextAssignment)

SubmissionsApi.submitCourseAssignment(
submissionType = SubmissionType.ONLINE_TEXT_ENTRY,
courseId = course.id,
assignmentId = pointsTextAssignment.id,
studentToken = student.token,
fileIds = emptyList<Long>().toMutableList()
)

assignmentDetailsPage.refresh()
assignmentDetailsPage.verifyAssignmentSubmitted()

// Let's grade the assignment
val textGrade = SubmissionsApi.gradeSubmission(
teacherToken = teacher.token,
courseId = course.id,
assignmentId = pointsTextAssignment.id,
studentId = student.id,
postedGrade = "13",
excused = false
)

assignmentDetailsPage.refresh()
assignmentDetailsPage.verifyAssignmentGraded("13")

Espresso.pressBack() // Back to assignment list

// Upload a text file for submission
assignmentListPage.clickAssignment(percentageFileAssignment)
val uploadInfo = uploadTextFile(
courseId = course.id,
assignmentId = percentageFileAssignment.id,
token = student.token,
fileUploadType = FileUploadType.ASSIGNMENT_SUBMISSION
)

// Submit the assignment
SubmissionsApi.submitCourseAssignment(
submissionType = SubmissionType.ONLINE_UPLOAD,
courseId = course.id,
assignmentId = percentageFileAssignment.id,
fileIds = listOf(uploadInfo.id).toMutableList(),
studentToken = student.token
)

// Verify that assignment has been submitted
assignmentDetailsPage.refresh()
assignmentDetailsPage.verifyAssignmentSubmitted()

// Grade the assignment
val fileGrade = SubmissionsApi.gradeSubmission(
teacherToken = teacher.token,
courseId = course.id,
assignmentId = percentageFileAssignment.id,
studentId = student.id,
postedGrade = "22",
excused = false
)

// Verify that the assignment has been graded
assignmentDetailsPage.refresh()
assignmentDetailsPage.verifyAssignmentGraded("22")

// Back to assignment list page
Espresso.pressBack()

// Let's verify that the assignments in the list all have grades now
assignmentListPage.refresh()
assignmentListPage.assertHasAssignment(pointsTextAssignment, textGrade.grade)
assignmentListPage.assertHasAssignment(letterGradeTextAssignment, submissionGrade.grade)
assignmentListPage.assertHasAssignment(percentageFileAssignment, fileGrade.grade)

// Let's make sure that comments are working
assignmentListPage.clickAssignment(percentageFileAssignment)
assignmentDetailsPage.goToSubmissionDetails()
submissionDetailsPage.openComments()
submissionDetailsPage.assertCommentDisplayed(
uploadInfo.fileName,
student)

// Add a comment, make sure it shows up in the stream
submissionDetailsPage.addAndSendComment("My comment!!")
sleep(2000) // Give the comment time to propagate
submissionDetailsPage.assertCommentDisplayed(
"My comment!!",
student
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,47 @@
*/
package com.instructure.student.ui.pages

import android.view.View
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.instructure.espresso.OnViewWithId
import com.instructure.espresso.assertContainsText
import com.instructure.espresso.assertDisplayed
import com.instructure.espresso.click
import com.instructure.espresso.closeSoftKeyboard
import com.instructure.espresso.page.BasePage
import com.instructure.espresso.scrollTo
import com.instructure.espresso.swipeDown
import com.instructure.espresso.typeText
import com.instructure.student.R
import org.hamcrest.Matcher
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.containsString

open class AssignmentDetailsPage : BasePage(R.id.assignmentDetailsPage) {
fun verifyAssignmentSubmitted() {
onView(withText(R.string.submissionStatusSuccessTitle)).assertDisplayed()
onView(allOf(withId(R.id.submissionStatus), withText(R.string.submitted))).assertDisplayed()
}

fun verifyAssignmentGraded(score: String) {
onView(allOf(withId(R.id.gradeContainer), isDisplayed())).scrollTo().assertDisplayed()
onView(allOf(withId(R.id.score), isDisplayed())).scrollTo().assertContainsText(score)
onView(allOf(withId(R.id.submissionStatus), withText(R.string.gradedSubmissionLabel))).assertDisplayed()
}

fun refresh() {
onView(allOf(withId(R.id.swipeRefreshLayout), isDisplayed())).swipeDown()
}

fun goToSubmissionDetails() {
onView(withId(R.id.submissionAndRubricLabel)).scrollTo().click()
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,24 @@
*/
package com.instructure.student.ui.pages

import android.view.View
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withChild
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withParent
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.instructure.dataseeding.model.AssignmentApiModel
import com.instructure.student.R
import com.instructure.espresso.*
import com.instructure.espresso.page.BasePage
import com.instructure.espresso.page.waitForViewWithText
import org.hamcrest.Matcher
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.containsString

class AssignmentListPage : BasePage(pageResId = R.id.assignmentListPage) {

Expand All @@ -43,8 +56,44 @@ class AssignmentListPage : BasePage(pageResId = R.id.assignmentListPage) {
emptyView.assertDisplayed()
}

fun assertHasAssignment(assignment: AssignmentApiModel) {
fun assertHasAssignment(assignment: AssignmentApiModel, expectedGrade: String? = null) {

waitForViewWithText(assignment.name).assertDisplayed()

// Check that either the assignment due date is present, or "No Due Date" is displayed
if(assignment.dueAt != null) {
val matcher = allOf(
withText(containsString("Due: ")),
withId(R.id.date),
withParent(withParent(withChild(withText(assignment.name)))))
scrollToAndAssertDisplayed(matcher)
}
else {
val matcher = allOf(
withText(R.string.toDoNoDueDate),
withId(R.id.date),
withParent(withParent(withChild(withText(assignment.name)))))
scrollToAndAssertDisplayed(matcher)
}

// Check that grade is present, if that is specified
if(expectedGrade != null) {
val matcher = allOf(
withText(containsString(expectedGrade)), // grade might be "13", total string "13/15"
withId(R.id.points),
withParent(withParent(withChild(withText(assignment.name)))))
scrollToAndAssertDisplayed(matcher)
}
}

fun refresh() {
onView(allOf(withId(R.id.swipeRefreshLayout), isDisplayed())).swipeDown()
}

private fun scrollToAndAssertDisplayed(matcher: Matcher<View>) {
onView(allOf(withId(R.id.listView), ViewMatchers.isDisplayed()))
.perform(RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(ViewMatchers.hasDescendant(matcher)))
.assertDisplayed()
}

fun assertHasGradingPeriods() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.instructure.student.ui.pages

import android.view.View
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.instructure.espresso.click
import com.instructure.espresso.page.BasePage
import com.instructure.student.R
import org.hamcrest.Matcher
import org.hamcrest.Matchers.allOf

class CourseBrowserPage : BasePage(R.id.courseBrowserPage) {

fun selectAssignments() {
val matcher = allOf(withText("Assignments"))
selectSection(matcher)
}

private fun selectSection(matcher: Matcher<View>) {
// Scroll RecyclerView item into view, if necessary
onView(allOf(withId(R.id.courseBrowserRecyclerView), isDisplayed()))
.perform(RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(hasDescendant(matcher)))

onView(matcher).click()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,10 @@ class DashboardPage : BasePage(R.id.dashboardPage) {
Espresso.onView(matcher).assertDisplayed()
}

fun selectCourse(course: CourseApiModel) {
assertDisplaysCourse(course)
onView(withText(course.name)).click()
}


}
Loading

0 comments on commit d1f1af8

Please sign in to comment.