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
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.instructure.canvas.espresso.annotations.E2E
import com.instructure.canvas.espresso.pressBackButton
import com.instructure.dataseeding.api.AssignmentsApi
import com.instructure.dataseeding.api.CustomStatusApi
import com.instructure.dataseeding.api.DifferentiationTagsApi
import com.instructure.dataseeding.api.SubmissionsApi
import com.instructure.dataseeding.model.GradingType
import com.instructure.dataseeding.model.SubmissionType
Expand Down Expand Up @@ -131,6 +132,218 @@ class CustomStatusesE2ETest: TeacherComposeTest() {
speedGraderGradePage.assertCurrentStatus("Graded", student.name)
}

@E2E
@Test
@TestMetaData(Priority.COMMON, FeatureCategory.CUSTOM_STATUSES, TestCategory.E2E)
fun testFilterCustomStatusesAndDifferentiationTagsE2E() {

Log.d(PREPARATION_TAG, "Seeding data.")
val data = seedData(teachers = 1, courses = 1, students = 3)
val student = data.studentsList[0]
val student2 = data.studentsList[1]
val studentWithoutTag = data.studentsList[2]
val teacher = data.teachersList[0]
val course = data.coursesList[0]

Log.d(PREPARATION_TAG, "Seeding a custom status ('AMAZING') with the admin user.")
customStatusId = CustomStatusApi.upsertCustomGradeStatus(adminToken, name = "AMAZING", color = "#FF0000")

Log.d(PREPARATION_TAG, "Create 'Differentiation Tags Group Set' differentiation group set for '${course.name}' course.")
val groupSetId = DifferentiationTagsApi.createGroupSet(
token = teacher.token,
courseId = course.id.toString(),
name = "Differentiation Tags Group Set",
nonCollaborative = true
)

Log.d(PREPARATION_TAG, "Seeding 'First Diff Tag' differentiation tags for '${course.name}' course.")
val firstDifferentiationTag = DifferentiationTagsApi.createGroup(
token = teacher.token,
groupSetId = groupSetId,
name = "First Diff Tag"
)

Log.d(PREPARATION_TAG, "Seeding 'Second Diff Tag' differentiation tags for '${course.name}' course.")
val secondDifferentiationTag = DifferentiationTagsApi.createGroup(
token = teacher.token,
groupSetId = groupSetId,
name = "Second Diff Tag"
)

Log.d(PREPARATION_TAG, "Seeding 'Third Diff Tag' differentiation tags for '${course.name}' course.")
val thirdDifferentiationTag = DifferentiationTagsApi.createGroup(
token = teacher.token,
groupSetId = groupSetId,
name = "Third Diff Tag"
)

Log.d(PREPARATION_TAG, "Assigning 'First Diff Tag' differentiation tag to '${student.name}' student.")
DifferentiationTagsApi.addUserToGroup(
token = teacher.token,
groupId = firstDifferentiationTag.toLong(),
userId = student.id
)

Log.d(PREPARATION_TAG, "Assigning 'Second Diff Tag' differentiation tag to '${student2.name}' student.")
DifferentiationTagsApi.addUserToGroup(
token = teacher.token,
groupId = secondDifferentiationTag.toLong(),
userId = student2.id
)

Log.d(PREPARATION_TAG, "Seeding 'Text Entry' assignment for '${course.name}' course.")
val testAssignment = AssignmentsApi.createAssignment(course.id, teacher.token, gradingType = GradingType.POINTS, pointsPossible = 15.0, dueAt = 1.days.fromNow.iso8601, submissionTypes = listOf(SubmissionType.ONLINE_TEXT_ENTRY))

Log.d(PREPARATION_TAG, "Student submits the assignment.")
SubmissionsApi.submitCourseAssignment(
courseId = course.id,
studentToken = student.token,
assignmentId = testAssignment.id,
submissionType = SubmissionType.ONLINE_TEXT_ENTRY
)

Log.d(PREPARATION_TAG, "Teacher grades submission with custom status 'AMAZING' for '${student.name}' student.")
SubmissionsApi.gradeSubmission(
teacherToken = teacher.token,
courseId = course.id,
assignmentId = testAssignment.id,
studentId = student.id,
postedGrade = "12",
customGradeStatusId = customStatusId
)

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

Log.d(STEP_TAG, "Open '${course.name}' course.")
dashboardPage.openCourse(course.name)

Log.d(STEP_TAG, "Navigate to '${course.name}' course's Assignments Tab.")
courseBrowserPage.openAssignmentsTab()

Log.d(STEP_TAG, "Click on '${testAssignment.name}' assignment.")
assignmentListPage.clickAssignment(testAssignment)

Log.d(STEP_TAG, "Open the 'All Submissions' page.")
assignmentDetailsPage.clickAllSubmissions()

Log.d(ASSERTION_TAG, "Assert that all the 3 students are displayed by default on the 'All Submissions' page before applying any filter.")
assignmentSubmissionListPage.assertHasSubmission(3)
assignmentSubmissionListPage.assertHasStudentSubmission(student)
assignmentSubmissionListPage.assertHasStudentSubmission(student2)
assignmentSubmissionListPage.assertHasStudentSubmission(studentWithoutTag)

Log.d(STEP_TAG, "Click on the filter icon on the top-right corner again.")
assignmentSubmissionListPage.clickFilterButton()

Log.d(ASSERTION_TAG, "Assert that all the custom status filter text is displayed among the filtering options.")
assignmentSubmissionListPage.assertCustomStatusFilterOption("AMAZING")

Log.d(ASSERTION_TAG, "Assert that all the corresponding differentiation tag filter texts are displayed among the filtering options.")
assignmentSubmissionListPage.assertDifferentiationTagFilterOption("Students without Differentiation tags")
assignmentSubmissionListPage.assertDifferentiationTagFilterOption("First Diff Tag")
assignmentSubmissionListPage.assertDifferentiationTagFilterOption("Second Diff Tag")
assignmentSubmissionListPage.assertDifferentiationTagFilterOption("Third Diff Tag")

// Check 'First Diff Tag' differentiation tag filter option
Log.d(STEP_TAG, "Select the 'First Diff Tag' differentiation tag filter and click on 'Done'.")
assignmentSubmissionListPage.clickDifferentiationTagFilter("First Diff Tag")
assignmentSubmissionListPage.clickFilterDialogDone()

Log.d(ASSERTION_TAG, "Assert that there is 1 submission displayed, and it is for '${student.name}' student since we applied a filter to the 'First Diff Tag' differentiation tag only.")
assignmentSubmissionListPage.assertHasSubmission(1)
assignmentSubmissionListPage.assertHasStudentSubmission(student)
assignmentSubmissionListPage.assertCustomStatusTag("AMAZING") // The displayed submission has the custom status tag 'AMAZING'
assignmentSubmissionListPage.assertStudentSubmissionNotDisplayed(student2)

// Check 'Second Diff Tag' differentiation tag filter option
Log.d(STEP_TAG, "Click on the filter icon on the top-right corner again.")
assignmentSubmissionListPage.clickFilterButton()

Log.d(STEP_TAG, "Unselect the 'First Diff Tag' and select the 'Second Diff Tag' differentiation tag filter and click on 'Done'.")
assignmentSubmissionListPage.clickDifferentiationTagFilter("First Diff Tag")
assignmentSubmissionListPage.clickDifferentiationTagFilter("Second Diff Tag")
assignmentSubmissionListPage.clickFilterDialogDone()

Log.d(ASSERTION_TAG, "Assert that there is 1 submission displayed, and it is for '${student2.name}' student since we applied a filter to the 'Second Diff Tag' differentiation tag only.")
assignmentSubmissionListPage.assertHasSubmission(1)
assignmentSubmissionListPage.assertHasStudentSubmission(student2)
assignmentSubmissionListPage.assertStudentSubmissionNotDisplayed(student)

// Check 'Students without Differentiation tags' filter option
Log.d(STEP_TAG, "Click on the filter icon on the top-right corner again.")
assignmentSubmissionListPage.clickFilterButton()

Log.d(STEP_TAG, "Unselect the 'Second Diff Tag' and select the 'Students without Differentiation tags' differentiation tag filter and click on 'Done'.")
assignmentSubmissionListPage.clickDifferentiationTagFilter("Second Diff Tag")
assignmentSubmissionListPage.clickDifferentiationTagFilter("Students without Differentiation tags")
assignmentSubmissionListPage.clickFilterDialogDone()

Log.d(ASSERTION_TAG, "Assert that there is 1 submission displayed, and it is for '${studentWithoutTag.name}' student since we applied the 'Students without Differentiation tags' filter.")
assignmentSubmissionListPage.assertHasSubmission(1)
assignmentSubmissionListPage.assertHasStudentSubmission(studentWithoutTag)
assignmentSubmissionListPage.assertStudentSubmissionNotDisplayed(student)
assignmentSubmissionListPage.assertStudentSubmissionNotDisplayed(student2)

// Check 'AMAZING' custom status filter option
Log.d(STEP_TAG, "Click on the filter icon on the top-right corner again.")
assignmentSubmissionListPage.clickFilterButton()

Log.d(STEP_TAG, "Unselect the 'Students without Differentiation tags' differentiation tag and select 'AMAZING' custom status filter and click on 'Done'.")
assignmentSubmissionListPage.clickDifferentiationTagFilter("Students without Differentiation tags")
assignmentSubmissionListPage.clickFilterCustomStatus("AMAZING")
assignmentSubmissionListPage.clickFilterDialogDone()

Log.d(ASSERTION_TAG, "Assert that there is 1 submission displayed, and it is for '${student.name}' student since we applied a filter to the 'First Diff Tag' differentiation tag only.")
assignmentSubmissionListPage.assertHasSubmission(1)
assignmentSubmissionListPage.assertHasStudentSubmission(student)
assignmentSubmissionListPage.assertCustomStatusTag("AMAZING") // The displayed submission has the custom status tag 'AMAZING'
assignmentSubmissionListPage.assertStudentSubmissionNotDisplayed(student2)

// Check 'Third Diff Tag' differentiation tag filter option
Log.d(STEP_TAG, "Click on the filter icon on the top-right corner again.")
assignmentSubmissionListPage.clickFilterButton()

Log.d(STEP_TAG, "Unselect the 'AMAZING' custom status filter option and select the 'Third Diff Tag' differentiation tag and click on 'Done'.")
assignmentSubmissionListPage.clickFilterCustomStatus("AMAZING")
assignmentSubmissionListPage.clickDifferentiationTagFilter("Third Diff Tag")
assignmentSubmissionListPage.clickFilterDialogDone()

Log.d(ASSERTION_TAG, "Assert that there is no submission displayed since there are no students with 'Third Diff Tag' differentiation tag, so the empty view is displayed.")
assignmentSubmissionListPage.assertHasNoSubmission()
assignmentSubmissionListPage.assertEmptyViewDisplayed()

// Check 'Missing' (aka. 'Not Submitted') status filter option AND 'Second Diff Tag' differentiation tag filter option together
// Important info: Filter groups behave with AND logic between them and OR logic within them.
Log.d(STEP_TAG, "Click on the filter icon on the top-right corner again.")
assignmentSubmissionListPage.clickFilterButton()

Log.d(STEP_TAG, "Select the 'Missing' status filter and 'Second Diff Tag' differentiation tag and click on 'Done'.")
assignmentSubmissionListPage.clickFilterNotSubmitted()
assignmentSubmissionListPage.clickDifferentiationTagFilter("Second Diff Tag")
assignmentSubmissionListPage.clickFilterDialogDone()

Log.d(ASSERTION_TAG, "Assert that there is 1 submission displayed, one for '${student2.name}' student since we applied the 'Missing' status filter and 'Second Diff Tag' differentiation tag filter simultaneously.")
assignmentSubmissionListPage.assertHasSubmission(1)
assignmentSubmissionListPage.assertHasStudentSubmission(student2)
assignmentSubmissionListPage.assertStudentSubmissionNotDisplayed(student)
assignmentSubmissionListPage.assertStudentSubmissionNotDisplayed(studentWithoutTag)

// Check 'AMAZING' custom status filter option AND 'Second Diff Tag' differentiation tag filter option together to check the AND logic between filter groups
Log.d(STEP_TAG, "Click on the filter icon on the top-right corner again.")
assignmentSubmissionListPage.clickFilterButton()

Log.d(STEP_TAG, "Unselect the 'Missing' status filter select 'AMAZING' custom status filter and click on 'Done'.")
assignmentSubmissionListPage.clickFilterNotSubmitted()
assignmentSubmissionListPage.clickFilterCustomStatus("AMAZING")
assignmentSubmissionListPage.clickFilterDialogDone()

Log.d(ASSERTION_TAG, "Assert that there is no submission displayed since there is no student submission which has the 'AMAZING' custom status and the 'Second Diff Tag' differentiation tag simultaneously.")
assignmentSubmissionListPage.assertHasNoSubmission()
assignmentSubmissionListPage.assertEmptyViewDisplayed()
}

@After
fun tearDown() {
customStatusId?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import androidx.compose.ui.test.swipeDown
import com.instructure.canvasapi2.models.User
import com.instructure.dataseeding.model.CanvasUserApiModel
import com.instructure.espresso.page.BasePage
import com.instructure.espresso.page.getStringFromResource
import com.instructure.teacher.R

/**
* Represents a page for managing assignment submissions.
Expand Down Expand Up @@ -192,6 +194,28 @@ class AssignmentSubmissionListPage(private val composeTestRule: ComposeTestRule)
.performClick()
}

/**
* Select a differentiation tag filter option.
* @param differentiationTagText The text of the differentiation tag to select. Defaults to "Students without differentiation tags".
*/
fun clickDifferentiationTagFilter(differentiationTagText: String = getStringFromResource(R.string.students_without_differentiation_tags)) {

val differentiationTagTestTag = if (differentiationTagText == getStringFromResource(R.string.students_without_differentiation_tags))
"includeWithoutTagsCheckBox"
else "differentiationTagCheckBox"

composeTestRule.onNode(
hasTestTag(differentiationTagTestTag) and hasAnySibling(
hasText(
differentiationTagText
)
),
useUnmergedTree = true
).performScrollTo()
.performClick()
composeTestRule.waitForIdle()
}

/**
* Assert that the corresponding submission filter options are displayed.
* @param filterName
Expand All @@ -203,13 +227,35 @@ class AssignmentSubmissionListPage(private val composeTestRule: ComposeTestRule)
).performScrollTo().assertIsDisplayed()
}

/**
* Assert that the corresponding custom status filter options are displayed.
* @param filterName Custom status filter name.
*/
fun assertCustomStatusFilterOption(filterName: String) {
composeTestRule.onNode(
hasTestTag("customStatusCheckBox") and hasAnySibling(hasText(filterName)),
useUnmergedTree = true
).performScrollTo().assertIsDisplayed()
}

/**
* Assert that the corresponding differentiation tag filter options are displayed.
* @param differentiationTagText The text of the differentiation tag to verify. Defaults to "Students without differentiation tags".
*/
fun assertDifferentiationTagFilterOption(differentiationTagText: String = getStringFromResource(R.string.students_without_differentiation_tags)) {
val differentiationTagTestTag = if (differentiationTagText == getStringFromResource(R.string.students_without_differentiation_tags))
"includeWithoutTagsCheckBox"
else "differentiationTagCheckBox"
composeTestRule.onNode(
hasTestTag(differentiationTagTestTag) and hasAnySibling(hasText(differentiationTagText)),
useUnmergedTree = true
).performScrollTo().assertIsDisplayed()
}

/**
* Assert that the corresponding precise filter options are displayed.
* @param filterName Precise filter name.
*/
fun assertPreciseFilterOption(filterName: String) {
composeTestRule.onNode(hasText(filterName), useUnmergedTree = true).performScrollTo()
.assertIsDisplayed()
Expand Down Expand Up @@ -439,6 +485,17 @@ class AssignmentSubmissionListPage(private val composeTestRule: ComposeTestRule)
.assertIsDisplayed()
}

/**
* Assert that a differentiation tag is displayed on a submission.
*
* @param tagName The name of the differentiation tag to verify.
*/
fun assertDifferentiationTag(tagName: String) {
composeTestRule.onNodeWithText(tagName, useUnmergedTree = true)
.performScrollTo()
.assertIsDisplayed()
}

/**
* Click on a differentiation tag filter option.
*
Expand Down Expand Up @@ -471,14 +528,4 @@ class AssignmentSubmissionListPage(private val composeTestRule: ComposeTestRule)
composeTestRule.waitForIdle()
}

/**
* Assert that a differentiation tag is displayed on a submission.
*
* @param tagName The name of the differentiation tag to verify.
*/
fun assertDifferentiationTag(tagName: String) {
composeTestRule.onNodeWithText(tagName, useUnmergedTree = true)
.performScrollTo()
.assertIsDisplayed()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#
# Copyright (C) 2026 - present Instructure, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

mutation createGroupInSet($groupSetId: ID!, $name: String!, $nonCollaborative: Boolean) {
createGroupInSet(input: {groupSetId: $groupSetId, name: $name, nonCollaborative: $nonCollaborative}) {
group {
_id
name
nonCollaborative
}
errors {
attribute
message
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#
# Copyright (C) 2026 - present Instructure, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

mutation createGroupSet($contextId: ID!, $contextType: GroupSetContextType!, $name: String!, $nonCollaborative: Boolean) {
createGroupSet(input: {contextId: $contextId, contextType: $contextType, name: $name, nonCollaborative: $nonCollaborative}) {
groupSet {
_id
name
nonCollaborative
}
errors {
attribute
message
}
}
}
Loading
Loading