diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/CourseBrowserE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/CourseBrowserE2ETest.kt
new file mode 100644
index 0000000000..2dc3d7e01c
--- /dev/null
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/CourseBrowserE2ETest.kt
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2025 - 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.
+ *
+ */
+package com.instructure.student.ui.e2e
+
+import android.util.Log
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performImeAction
+import androidx.compose.ui.test.performTextInput
+import androidx.compose.ui.test.requestFocus
+import androidx.test.espresso.Espresso
+import com.instructure.canvas.espresso.E2E
+import com.instructure.canvas.espresso.FeatureCategory
+import com.instructure.canvas.espresso.Priority
+import com.instructure.canvas.espresso.SecondaryFeatureCategory
+import com.instructure.canvas.espresso.TestCategory
+import com.instructure.canvas.espresso.TestMetaData
+import com.instructure.canvasapi2.models.SmartSearchContentType
+import com.instructure.canvasapi2.models.SmartSearchFilter
+import com.instructure.dataseeding.api.AssignmentsApi
+import com.instructure.dataseeding.api.DiscussionTopicsApi
+import com.instructure.dataseeding.api.PagesApi
+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.student.ui.utils.StudentComposeTest
+import com.instructure.student.ui.utils.seedData
+import com.instructure.student.ui.utils.tokenLogin
+import dagger.hilt.android.testing.HiltAndroidTest
+import org.junit.Test
+
+@HiltAndroidTest
+class CourseBrowserE2ETest : StudentComposeTest() {
+
+ override fun displaysPageObjects() = Unit
+
+ override fun enableAndConfigureAccessibilityChecks() = Unit
+
+ @E2E
+ @Test
+ @TestMetaData(Priority.MANDATORY, FeatureCategory.COURSE_BROWSER, TestCategory.E2E, SecondaryFeatureCategory.SMART_SEARCH)
+ fun testSmartSearchE2E() {
+
+ Log.d(PREPARATION_TAG,"Seeding 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]
+
+ Log.d(PREPARATION_TAG,"Seeding 'Text Entry' assignment for '${course.name}' course with 2 days ahead due date.")
+ val testAssignment = AssignmentsApi.createAssignment(course.id, teacher.token, dueAt = 2.days.fromNow.iso8601, pointsPossible = 15.0, submissionTypes = listOf(
+ SubmissionType.ONLINE_TEXT_ENTRY), assignmentName = "Test SmartSearch Assignment")
+
+ Log.d(STEP_TAG,"Seed an announcement for '${course.name}' course.")
+ val testAnnouncement = DiscussionTopicsApi.createAnnouncement(course.id, teacher.token, announcementTitle = "Test Announcement")
+
+ Log.d(PREPARATION_TAG,"Create a discussion topic for '${course.name}' course.")
+ val testDiscussion = DiscussionTopicsApi.createDiscussion(courseId = course.id, token = teacher.token, discussionTitle = "Test Discussion")
+
+ Log.d(PREPARATION_TAG,"Create a published page for course: '${course.name}'.")
+ val testPage = PagesApi.createCoursePage(course.id, teacher.token, published = true, frontPage = false, pageTitle = "Test SmartSearch Page", body = "
")
+
+ Log.d(STEP_TAG,"Login with user: '${student.name}', login id: '${student.loginId}'.")
+ tokenLogin(student)
+
+ Log.d(STEP_TAG,"Wait for the Dashboard Page to be rendered. Select course: '${course.name}'.")
+ dashboardPage.waitForRender()
+ Thread.sleep(10000) // Wait for the API creations to complete, sometimes the creation processes are too slow on the API side
+ dashboardPage.selectCourse(course)
+
+ Log.d(STEP_TAG,"Assert that the Course Browser Page is displayed.")
+ courseBrowserPage.assertPageObjects()
+
+ Log.d(STEP_TAG, "Click on the 'Smart Search' (Magnifying glass) icon on the top right corner of the Course Browser page.")
+ courseBrowserPage.clickOnSmartSearch()
+
+ val smartSearchText = testAssignment.name.take(4) // "Test" will be the search text.
+ Log.d(STEP_TAG, "Type the '$smartSearchText' into the search input field.")
+ composeTestRule.onNodeWithTag("searchField")
+ .requestFocus()
+ .performClick()
+ .performTextInput(smartSearchText)
+ composeTestRule.onNodeWithTag("searchField").performImeAction()
+ composeTestRule.waitForIdle()
+
+ Log.d(ASSERTION_TAG, "Assert that the '$smartSearchText' text is displayed in the search input field and the filter button is displayed on the search bar.")
+ smartSearchPage.assertQuery(smartSearchText)
+ composeTestRule.onNodeWithTag("filterButton").assertIsDisplayed()
+
+ Log.d(ASSERTION_TAG, "Assert that the '${course.name}' is displayed under the 'Results in course' section.")
+ smartSearchPage.assertCourse(course.name)
+
+ Log.d(ASSERTION_TAG, "Assert that the '${testAssignment.name}' assignment is displayed and it's type is 'Assignment'.")
+ smartSearchPage.assertItemDisplayed(testAssignment.name, "Assignment")
+
+ Log.d(ASSERTION_TAG, "Assert that the '${testAnnouncement.title}' announcement is displayed and it's type is 'Announcement'.")
+ smartSearchPage.assertItemDisplayed(testAnnouncement.title, "Announcement")
+
+ Log.d(ASSERTION_TAG, "Assert that the '${testDiscussion.title}' discussion is displayed and it's type is 'Discussion'.")
+ smartSearchPage.assertItemDisplayed(testDiscussion.title, "Discussion")
+
+ Log.d(ASSERTION_TAG, "Assert that the '${testPage.title}' page is displayed and it's type is 'Page'.")
+ smartSearchPage.assertItemDisplayed(testPage.title, "Page")
+
+ Log.d(STEP_TAG, "Click on the '${testAssignment.name}' assignment.")
+ smartSearchPage.clickOnItem(testAssignment.name)
+
+ Log.d(ASSERTION_TAG, "Assert that the previous click action will navigate to the '${testAssignment.name}' assignment's details page.")
+ assignmentDetailsPage.assertAssignmentDetails(testAssignment)
+
+ Log.d(STEP_TAG, "Navigate back to the Smart Search Result List page.")
+ Espresso.pressBack()
+
+ Log.d(STEP_TAG, "Click on the '${testAnnouncement.title}' announcement.")
+ smartSearchPage.clickOnItem(testAnnouncement.title)
+
+ Log.d(ASSERTION_TAG, "Assert that the previous click action will navigate to the '${testAnnouncement.title}' announcement's details page.")
+ discussionDetailsPage.assertToolbarDiscussionTitle(testAnnouncement.title)
+
+ Log.d(STEP_TAG, "Navigate back to the Smart Search Result List page.")
+ Espresso.pressBack()
+
+ Log.d(STEP_TAG, "Click on the '${testDiscussion.title}' discussion.")
+ smartSearchPage.clickOnItem(testDiscussion.title)
+
+ Log.d(ASSERTION_TAG, "Assert that the previous click action will navigate to the '${testDiscussion.title}' discussion's details page.")
+ discussionDetailsPage.assertToolbarDiscussionTitle(testDiscussion.title)
+
+ Log.d(STEP_TAG, "Navigate back to the Smart Search Result List page.")
+ Espresso.pressBack()
+
+ Log.d(STEP_TAG, "Click on the '${testPage.title}' page.")
+ smartSearchPage.clickOnItem(testPage.title)
+
+ Log.d(ASSERTION_TAG, "Assert that the previous click action will navigate to the '${testPage.title}' page's details page. (Assert for the URL as we are displaying the URL of the page when navigating to it's details page from a link.")
+ pageDetailsPage.assertToolbarTitle(testPage.url)
+
+ Log.d(STEP_TAG, "Navigate back to the Smart Search Result List page.")
+ Espresso.pressBack()
+
+ Log.d(STEP_TAG, "Click on the 'Filters' icon on the top-right corner.")
+ smartSearchPage.clickOnFilters()
+
+ Log.d(ASSERTION_TAG, "Assert that all of the types (Pages, Discussion Topics, Announcements, Assignments) are checked by default.")
+ smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.PAGES)
+ smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.DISCUSSION_TOPICS)
+ smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.ANNOUNCEMENTS)
+ smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.ASSIGNMENTS)
+
+ Log.d(STEP_TAG, "Click on the 'Pages' and 'Announcements' filters to turn them off. Apply the filters.")
+ smartSearchPreferencesPage.clickOnFilter(SmartSearchFilter.PAGES)
+ smartSearchPreferencesPage.clickOnFilter(SmartSearchFilter.ANNOUNCEMENTS)
+ smartSearchPreferencesPage.applyFilters()
+
+ Log.d(ASSERTION_TAG, "Assert that the 'Page' and 'Announcement' result items are not displayed any more on the Smart Search Results page but the 'Discussion Topic' and 'Assignment' are still displayed.")
+ smartSearchPage.assertItemNotDisplayed(testPage.title, "Page")
+ smartSearchPage.assertItemNotDisplayed(testAnnouncement.title, "Announcement")
+ smartSearchPage.assertItemDisplayed(testDiscussion.title, "Discussion")
+ smartSearchPage.assertItemDisplayed(testAssignment.name, "Assignment")
+
+ Log.d(STEP_TAG, "Click on the 'Filters' icon on the top-right corner.")
+ smartSearchPage.clickOnFilters()
+
+ Log.d(ASSERTION_TAG, "Assert that the 'Pages' and 'Announcements' filters are unchecked (as we modified it recently) and the 'Discussion Topics' and 'Assignments' are remain checked.")
+ smartSearchPreferencesPage.assertFilterNotChecked(SmartSearchFilter.PAGES)
+ smartSearchPreferencesPage.assertFilterNotChecked(SmartSearchFilter.ANNOUNCEMENTS)
+ smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.DISCUSSION_TOPICS)
+ smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.ASSIGNMENTS)
+
+ Log.d(STEP_TAG, "Click on the 'Discussion Topic' and 'Assignment' filters to turn them off as well. Apply the new filters.")
+ smartSearchPreferencesPage.clickOnFilter(SmartSearchFilter.DISCUSSION_TOPICS)
+ smartSearchPreferencesPage.clickOnFilter(SmartSearchFilter.ASSIGNMENTS)
+ smartSearchPreferencesPage.applyFilters()
+
+ Log.d(ASSERTION_TAG, "Assert that all the types of result items are displayed as we have a logic if nothing is selected then we show every item on the Smart Search Result page.")
+ smartSearchPage.assertItemDisplayed(testPage.title, "Page")
+ smartSearchPage.assertItemDisplayed(testAnnouncement.title, "Announcement")
+ smartSearchPage.assertItemDisplayed(testDiscussion.title, "Discussion")
+ smartSearchPage.assertItemDisplayed(testAssignment.name, "Assignment")
+
+ Log.d(STEP_TAG, "Click on the 'Filters' icon on the top-right corner.")
+ smartSearchPage.clickOnFilters()
+
+ Log.d(ASSERTION_TAG, "Assert that none of the type filters are checked.")
+ smartSearchPreferencesPage.assertFilterNotChecked(SmartSearchFilter.PAGES)
+ smartSearchPreferencesPage.assertFilterNotChecked(SmartSearchFilter.ANNOUNCEMENTS)
+ smartSearchPreferencesPage.assertFilterNotChecked(SmartSearchFilter.DISCUSSION_TOPICS)
+ smartSearchPreferencesPage.assertFilterNotChecked(SmartSearchFilter.ASSIGNMENTS)
+
+ Log.d(STEP_TAG, "Click on the 'Select All' button.")
+ smartSearchPreferencesPage.toggleAll()
+
+ Log.d(ASSERTION_TAG, "Assert that all of the type filters are checked.")
+ smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.PAGES)
+ smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.ANNOUNCEMENTS)
+ smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.DISCUSSION_TOPICS)
+ smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.ASSIGNMENTS)
+
+ Log.d(ASSERTION_TAG, "Assert that the 'Sort By' section displayed properly. By default, the 'Relevance' radiobutton should be selected.")
+ smartSearchPreferencesPage.assertSortByDetails()
+ smartSearchPreferencesPage.assertRadioButtonSelected("Relevance")
+
+ Log.d(STEP_TAG, "Select the 'Type' sorting type and apply the filters.")
+ smartSearchPreferencesPage.selectTypeSortType()
+ smartSearchPreferencesPage.applyFilters()
+
+ Log.d(ASSERTION_TAG, "Assert that the four different group header titles (Pages, Discussion Topics, Announcements, Assignments) are displayed.")
+ smartSearchPage.assertGroupHeaderDisplayed(SmartSearchContentType.WIKI_PAGE)
+ smartSearchPage.assertGroupHeaderDisplayed(SmartSearchContentType.DISCUSSION_TOPIC)
+ smartSearchPage.assertGroupHeaderDisplayed(SmartSearchContentType.ANNOUNCEMENT)
+ smartSearchPage.assertGroupHeaderDisplayed(SmartSearchContentType.ASSIGNMENT)
+
+ Log.d(ASSERTION_TAG, "Assert that all the types of result items are displayed on the Smart Search Result page.")
+ smartSearchPage.assertItemDisplayed(testPage.title, "Page")
+ smartSearchPage.assertItemDisplayed(testAnnouncement.title, "Announcement")
+ smartSearchPage.assertItemDisplayed(testDiscussion.title, "Discussion")
+ smartSearchPage.assertItemDisplayed(testAssignment.name, "Assignment")
+ }
+
+}
\ No newline at end of file
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CourseBrowserPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CourseBrowserPage.kt
index f462ea98f7..5a3e147703 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CourseBrowserPage.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CourseBrowserPage.kt
@@ -43,6 +43,7 @@ import com.instructure.espresso.click
import com.instructure.espresso.page.BasePage
import com.instructure.espresso.page.onView
import com.instructure.espresso.page.plus
+import com.instructure.espresso.page.withAncestor
import com.instructure.espresso.page.withId
import com.instructure.espresso.scrollTo
import com.instructure.espresso.swipeUp
@@ -127,6 +128,10 @@ open class CourseBrowserPage : BasePage(R.id.courseBrowserPage) {
onView(matcher).click()
}
+ fun clickOnSmartSearch() {
+ onView(withId(R.id.searchBar) + withAncestor(R.id.courseBrowserPage)).click()
+ }
+
fun assertTitleCorrect(course: Course) {
// You might have multiple of these if you navigate from one course to another.
// In that event, we'll have to choose the one that is displayed.
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentComposeTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentComposeTest.kt
index 8ca9a65d74..89eacca2ba 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentComposeTest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentComposeTest.kt
@@ -31,6 +31,8 @@ import com.instructure.canvas.espresso.common.pages.compose.InboxDetailsPage
import com.instructure.canvas.espresso.common.pages.compose.RecipientPickerPage
import com.instructure.canvas.espresso.common.pages.compose.SelectContextPage
import com.instructure.canvas.espresso.common.pages.compose.SettingsPage
+import com.instructure.canvas.espresso.common.pages.compose.SmartSearchPage
+import com.instructure.canvas.espresso.common.pages.compose.SmartSearchPreferencesPage
import com.instructure.student.activity.LoginActivity
import org.junit.Rule
@@ -51,4 +53,6 @@ abstract class StudentComposeTest : StudentTest() {
val inboxComposePage = InboxComposePage(composeTestRule)
val recipientPickerPage = RecipientPickerPage(composeTestRule)
val selectContextPage = SelectContextPage(composeTestRule)
+ val smartSearchPage = SmartSearchPage(composeTestRule)
+ val smartSearchPreferencesPage = SmartSearchPreferencesPage(composeTestRule)
}
\ No newline at end of file
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt
index 0d083d2fd3..62c02690d0 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt
@@ -31,7 +31,7 @@ import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Test
@HiltAndroidTest
-class ModulesE2ETest : TeacherComposeTest() {
+class ModulesE2ETest : TeacherComposeTest() {
override fun displaysPageObjects() = Unit
diff --git a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/AssignmentsApi.kt b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/AssignmentsApi.kt
index b665471602..ce3e036a2d 100644
--- a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/AssignmentsApi.kt
+++ b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/AssignmentsApi.kt
@@ -17,7 +17,13 @@
package com.instructure.dataseeding.api
-import com.instructure.dataseeding.model.*
+import com.instructure.dataseeding.model.AssignmentApiModel
+import com.instructure.dataseeding.model.AssignmentOverrideApiModel
+import com.instructure.dataseeding.model.CreateAssignmentOverrideForStudents
+import com.instructure.dataseeding.model.CreateAssignmentOverrideForStudentsWrapper
+import com.instructure.dataseeding.model.CreateAssignmentWrapper
+import com.instructure.dataseeding.model.GradingType
+import com.instructure.dataseeding.model.SubmissionType
import com.instructure.dataseeding.util.CanvasNetworkAdapter
import com.instructure.dataseeding.util.Randomizer
import retrofit2.Call
@@ -48,6 +54,7 @@ object AssignmentsApi {
val gradingType: GradingType = GradingType.POINTS,
val allowedExtensions: List? = null,
val teacherToken: String,
+ val assignmentName: String? = null,
val groupCategoryId: Long? = null,
val pointsPossible: Double? = null,
val importantDate: Boolean? = null,
@@ -64,6 +71,7 @@ object AssignmentsApi {
request.submissionTypes,
request.gradingType,
request.allowedExtensions,
+ request.assignmentName,
request.groupCategoryId,
request.pointsPossible,
request.importantDate,
@@ -81,11 +89,13 @@ object AssignmentsApi {
submissionTypes: List = emptyList(),
gradingType: GradingType = GradingType.POINTS,
allowedExtensions: List? = null,
+ assignmentName: String? = null,
groupCategoryId: Long? = null,
pointsPossible: Double? = null,
importantDate: Boolean? = null,
assignmentGroupId: Long? = null): AssignmentApiModel {
val assignment = CreateAssignmentWrapper(Randomizer.randomAssignment(
+ assignmentName,
withDescription,
lockAt,
unlockAt,
diff --git a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/DiscussionTopicsApi.kt b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/DiscussionTopicsApi.kt
index d15c38a850..33846d98e7 100644
--- a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/DiscussionTopicsApi.kt
+++ b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/DiscussionTopicsApi.kt
@@ -62,16 +62,16 @@ object DiscussionTopicsApi {
.body()!!
}
- fun createDiscussion(courseId: Long, token: String, isAnnouncement: Boolean = false, lockedForUser: Boolean = false, locked: Boolean = false): DiscussionApiModel {
- val discussionTopic = Randomizer.randomDiscussion(isAnnouncement, lockedForUser, locked)
+ fun createDiscussion(courseId: Long, token: String, isAnnouncement: Boolean = false, lockedForUser: Boolean = false, locked: Boolean = false, discussionTitle: String? = null): DiscussionApiModel {
+ val discussionTopic = Randomizer.randomDiscussion(discussionTitle, isAnnouncement, lockedForUser, locked)
return discussionTopicsService(token)
.createDiscussionTopic(courseId, discussionTopic)
.execute()
.body()!!
}
- fun createAnnouncement(courseId: Long, token: String, lockedForUser: Boolean = false, locked: Boolean = false): DiscussionApiModel {
- val discussion = createDiscussion(courseId, token, true, lockedForUser, locked, )
+ fun createAnnouncement(courseId: Long, token: String, lockedForUser: Boolean = false, locked: Boolean = false, announcementTitle: String? = null): DiscussionApiModel {
+ val discussion = createDiscussion(courseId, token, true, lockedForUser, locked, announcementTitle)
if(!lockedForUser) {
return DiscussionApiModel(
diff --git a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/PagesApi.kt b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/PagesApi.kt
index 5c5353a4a0..209a942393 100644
--- a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/PagesApi.kt
+++ b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/api/PagesApi.kt
@@ -44,8 +44,9 @@ object PagesApi {
frontPage: Boolean = false,
body: String = Randomizer.randomPageBody(),
editingRoles: String? = null,
+ pageTitle: String? = null
): PageApiModel {
- val page = CreatePageWrapper(CreatePage(Randomizer.randomPageTitle(), body, published, frontPage, editingRoles))
+ val page = CreatePageWrapper(CreatePage(pageTitle ?: Randomizer.randomPageTitle(), body, published, frontPage, editingRoles))
return pagesService(token)
.createCoursePage(courseId, page)
diff --git a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/Randomizer.kt b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/Randomizer.kt
index 85a536e81d..0cb7b0813d 100644
--- a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/Randomizer.kt
+++ b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/Randomizer.kt
@@ -27,7 +27,9 @@ import com.instructure.dataseeding.model.CreateSubmissionComment
import com.instructure.dataseeding.model.GradingType
import com.instructure.dataseeding.model.SubmissionType
import com.instructure.dataseeding.model.SubmitCourseAssignmentSubmission
-import java.util.*
+import java.util.Date
+import java.util.Locale
+import java.util.UUID
object Randomizer {
private val faker = Faker()
@@ -56,9 +58,9 @@ object Randomizer {
fun randomImageUrlSmall(): String = faker.internet().image(64, 64, false, null)
- fun randomDiscussion(isAnnouncement: Boolean = false, lockedForUser: Boolean = false, locked: Boolean = false): CreateDiscussionTopic =
+ fun randomDiscussion(discussionTitle: String?, isAnnouncement: Boolean = false, lockedForUser: Boolean = false, locked: Boolean = false): CreateDiscussionTopic =
CreateDiscussionTopic(
- title = faker.lorem().sentence(),
+ title = discussionTitle ?: faker.lorem().sentence(),
message = faker.lorem().paragraph(),
isAnnouncement = isAnnouncement,
lockedForUser = lockedForUser,
@@ -77,9 +79,9 @@ object Randomizer {
fun randomGradingPeriodSetTitle(): String = "${faker.pokemon().location()} Set"
fun randomGradingPeriodName(): String = "${faker.pokemon().name()} Grading Period"
- fun randomAssignment(withDescription: Boolean = false, lockAt: String, unlockAt: String, dueAt: String, submissionTypes: List, gradingType: GradingType?, groupCategoryId: Long?, pointsPossible: Double?, allowedExtensions: List?, importantDate: Boolean?, assignmentGroupId: Long? = null): CreateAssignment =
+ fun randomAssignment(assignmentName: String?, withDescription: Boolean = false, lockAt: String, unlockAt: String, dueAt: String, submissionTypes: List, gradingType: GradingType?, groupCategoryId: Long?, pointsPossible: Double?, allowedExtensions: List?, importantDate: Boolean?, assignmentGroupId: Long? = null): CreateAssignment =
CreateAssignment(
- name = faker.lorem().sentence(),
+ name = assignmentName ?: faker.lorem().sentence(),
description = if (withDescription) faker.lorem().paragraph() else null,
lockAt = if (lockAt.isNotBlank()) lockAt else null,
unlockAt = if (unlockAt.isNotBlank()) unlockAt else null,
@@ -87,7 +89,7 @@ object Randomizer {
submissionTypes = if (submissionTypes.isEmpty()) null else submissionTypes.map {
if (it.name == "NO_TYPE") "none" else it.name.lowercase(Locale.getDefault())
},
- gradingType = if (gradingType != null) gradingType.toString().lowercase(Locale.getDefault()) else "points",
+ gradingType = gradingType?.toString()?.lowercase(Locale.getDefault()) ?: "points",
groupCategoryId = groupCategoryId,
pointsPossible = pointsPossible,
allowedExtensions = allowedExtensions,
diff --git a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/TestMetaData.kt b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/TestMetaData.kt
index 5b97f23d01..773eb53e39 100644
--- a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/TestMetaData.kt
+++ b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/TestMetaData.kt
@@ -34,7 +34,7 @@ enum class FeatureCategory {
ASSIGNMENTS, SUBMISSIONS, LOGIN, COURSE, DASHBOARD, GROUPS, SETTINGS, PAGES, DISCUSSIONS, MODULES, CALENDAR,
INBOX, GRADES, FILES, EVENTS, PEOPLE, CONFERENCES, COLLABORATIONS, SYLLABUS, TODOS, QUIZZES, NOTIFICATIONS,
ANNOTATIONS, ANNOUNCEMENTS, COMMENTS, BOOKMARKS, NONE, CANVAS_FOR_ELEMENTARY, SPEED_GRADER, SYNC_SETTINGS, SYNC_PROGRESS, OFFLINE_CONTENT, LEFT_SIDE_MENU,
- COURSE_LIST
+ COURSE_LIST, COURSE_BROWSER
}
enum class SecondaryFeatureCategory {
@@ -43,7 +43,7 @@ enum class SecondaryFeatureCategory {
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,
- MODULES_ASSIGNMENTS, MODULES_DISCUSSIONS, MODULES_FILES, MODULES_PAGES, MODULES_QUIZZES, OFFLINE_MODE, ALL_COURSES, CHANGE_USER, ASSIGNMENT_REMINDER, ASSIGNMENT_DETAILS, ADD_STUDENT
+ MODULES_ASSIGNMENTS, MODULES_DISCUSSIONS, MODULES_FILES, MODULES_PAGES, MODULES_QUIZZES, OFFLINE_MODE, ALL_COURSES, CHANGE_USER, ASSIGNMENT_REMINDER, ASSIGNMENT_DETAILS, SMART_SEARCH, ADD_STUDENT
}
enum class TestCategory {
diff --git a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/interaction/SmartSearchInteractionTest.kt b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/interaction/SmartSearchInteractionTest.kt
index 99d505b912..6c801164a8 100644
--- a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/interaction/SmartSearchInteractionTest.kt
+++ b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/interaction/SmartSearchInteractionTest.kt
@@ -180,14 +180,14 @@ abstract class SmartSearchInteractionTest : CanvasComposeTest() {
smartSearchPage.assertItemDisplayed("Test Announcement for query", "Announcement")
smartSearchPage.assertItemDisplayed("Test Assignment for query", "Assignment")
- smartSearchPage.openFilters()
+ smartSearchPage.clickOnFilters()
smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.PAGES)
smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.DISCUSSION_TOPICS)
smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.ANNOUNCEMENTS)
smartSearchPreferencesPage.assertFilterChecked(SmartSearchFilter.ASSIGNMENTS)
- smartSearchPreferencesPage.toggleFilter(SmartSearchFilter.PAGES)
+ smartSearchPreferencesPage.clickOnFilter(SmartSearchFilter.PAGES)
smartSearchPreferencesPage.applyFilters()
smartSearchPage.assertItemNotDisplayed("Test Page for query", "Page")
@@ -195,11 +195,11 @@ abstract class SmartSearchInteractionTest : CanvasComposeTest() {
smartSearchPage.assertItemDisplayed("Test Announcement for query", "Announcement")
smartSearchPage.assertItemDisplayed("Test Assignment for query", "Assignment")
- smartSearchPage.openFilters()
+ smartSearchPage.clickOnFilters()
smartSearchPreferencesPage.assertFilterNotChecked(SmartSearchFilter.PAGES)
- smartSearchPreferencesPage.toggleFilter(SmartSearchFilter.ASSIGNMENTS)
+ smartSearchPreferencesPage.clickOnFilter(SmartSearchFilter.ASSIGNMENTS)
smartSearchPreferencesPage.applyFilters()
smartSearchPage.assertItemNotDisplayed("Test Page for query", "Page")
@@ -207,7 +207,7 @@ abstract class SmartSearchInteractionTest : CanvasComposeTest() {
smartSearchPage.assertItemDisplayed("Test Announcement for query", "Announcement")
smartSearchPage.assertItemNotDisplayed("Test Assignment for query", "Assignment")
- smartSearchPage.openFilters()
+ smartSearchPage.clickOnFilters()
smartSearchPreferencesPage.toggleAll()
smartSearchPreferencesPage.applyFilters()
@@ -216,7 +216,7 @@ abstract class SmartSearchInteractionTest : CanvasComposeTest() {
smartSearchPage.assertItemDisplayed("Test Announcement for query", "Announcement")
smartSearchPage.assertItemDisplayed("Test Assignment for query", "Assignment")
- smartSearchPage.openFilters()
+ smartSearchPage.clickOnFilters()
smartSearchPreferencesPage.toggleAll()
smartSearchPreferencesPage.applyFilters()
@@ -260,7 +260,7 @@ abstract class SmartSearchInteractionTest : CanvasComposeTest() {
composeTestRule.waitForIdle()
- smartSearchPage.openFilters()
+ smartSearchPage.clickOnFilters()
smartSearchPreferencesPage.selectTypeSortType()
smartSearchPreferencesPage.applyFilters()
@@ -300,7 +300,7 @@ abstract class SmartSearchInteractionTest : CanvasComposeTest() {
composeTestRule.waitForIdle()
- smartSearchPage.openFilters()
+ smartSearchPage.clickOnFilters()
smartSearchPreferencesPage.selectTypeSortType()
smartSearchPreferencesPage.applyFilters()
@@ -357,7 +357,7 @@ abstract class SmartSearchInteractionTest : CanvasComposeTest() {
composeTestRule.waitForIdle()
- smartSearchPage.openFilters()
+ smartSearchPage.clickOnFilters()
smartSearchPreferencesPage.selectTypeSortType()
smartSearchPreferencesPage.applyFilters()
@@ -366,7 +366,7 @@ abstract class SmartSearchInteractionTest : CanvasComposeTest() {
smartSearchPage.assertGroupHeaderDisplayed(SmartSearchContentType.ANNOUNCEMENT)
smartSearchPage.assertGroupHeaderDisplayed(SmartSearchContentType.ASSIGNMENT)
- smartSearchPage.openFilters()
+ smartSearchPage.clickOnFilters()
smartSearchPreferencesPage.selectRelevanceSortType()
smartSearchPreferencesPage.applyFilters()
diff --git a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/pages/compose/SmartSearchPage.kt b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/pages/compose/SmartSearchPage.kt
index c40a5c2210..06772dd936 100644
--- a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/pages/compose/SmartSearchPage.kt
+++ b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/pages/compose/SmartSearchPage.kt
@@ -90,7 +90,7 @@ class SmartSearchPage(private val composeTestRule: ComposeTestRule) : BasePage()
.performClick()
}
- fun openFilters() {
+ fun clickOnFilters() {
composeTestRule.onNode(
hasTestTag("filterButton"),
useUnmergedTree = true
diff --git a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/pages/compose/SmartSearchPreferencesPage.kt b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/pages/compose/SmartSearchPreferencesPage.kt
index 3f4e5491ba..246f14a6f4 100644
--- a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/pages/compose/SmartSearchPreferencesPage.kt
+++ b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/common/pages/compose/SmartSearchPreferencesPage.kt
@@ -15,19 +15,24 @@
*/
package com.instructure.canvas.espresso.common.pages.compose
+import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.assertIsSelected
import androidx.compose.ui.test.hasParent
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollToNode
import com.instructure.canvasapi2.models.SmartSearchFilter
class SmartSearchPreferencesPage(private val composeTestRule: ComposeTestRule) {
- fun toggleFilter(filter: SmartSearchFilter) {
+ // Actions
+
+ fun clickOnFilter(filter: SmartSearchFilter) {
composeTestRule.onNodeWithTag("preferencesScreen", useUnmergedTree = true)
.performScrollToNode(hasTestTag("${filter.name.lowercase()}FilterRow"))
composeTestRule.onNodeWithTag("${filter.name.lowercase()}FilterRow")
@@ -39,6 +44,23 @@ class SmartSearchPreferencesPage(private val composeTestRule: ComposeTestRule) {
.performClick()
}
+ fun toggleAll() {
+ composeTestRule.onNodeWithTag("toggleAllButton", useUnmergedTree = true)
+ .performClick()
+ }
+
+ fun selectRelevanceSortType() {
+ composeTestRule.onNodeWithTag("relevanceTypeSelector")
+ .performClick()
+ }
+
+ fun selectTypeSortType() {
+ composeTestRule.onNodeWithTag("typeTypeSelector")
+ .performClick()
+ }
+
+ // Assertions
+
fun assertFilterChecked(filter: SmartSearchFilter) {
composeTestRule.onNodeWithTag("preferencesScreen", useUnmergedTree = true)
.performScrollToNode(hasTestTag("${filter.name.lowercase()}FilterRow"))
@@ -59,18 +81,14 @@ class SmartSearchPreferencesPage(private val composeTestRule: ComposeTestRule) {
.assertIsOff()
}
- fun toggleAll() {
- composeTestRule.onNodeWithTag("toggleAllButton", useUnmergedTree = true)
- .performClick()
- }
-
- fun selectRelevanceSortType() {
- composeTestRule.onNodeWithTag("relevanceTypeSelector")
- .performClick()
+ fun assertSortByDetails() {
+ composeTestRule.onNodeWithText("Sort By").assertIsDisplayed()
+ composeTestRule.onNodeWithTag("relevanceTypeSelector").assertIsDisplayed()
+ composeTestRule.onNodeWithTag("typeTypeSelector").assertIsDisplayed()
}
- fun selectTypeSortType() {
- composeTestRule.onNodeWithTag("typeTypeSelector")
- .performClick()
+ fun assertRadioButtonSelected(sortText: String) {
+ if(sortText == "Relevance") composeTestRule.onNodeWithTag("relevanceRadioButton").assertIsSelected()
+ else if(sortText == "Type") composeTestRule.onNodeWithTag("typeRadioButton").assertIsSelected()
}
}
\ No newline at end of file