From 6c45bd3e29191461fe7920a00afcbb29942e3315 Mon Sep 17 00:00:00 2001 From: "adam.nagy" Date: Tue, 7 Apr 2026 17:08:15 +0200 Subject: [PATCH 1/2] Implement E2E test for Front Page tab visibility in K5 elementary courses. refs: MBL-19884 affects: Parent release note: --- .../compose/CourseDetailsFrontPageE2ETest.kt | 58 ++++++++++++++++++- .../ui/pages/classic/FrontPagePage.kt | 5 +- .../ui/pages/compose/CourseDetailsPage.kt | 17 +++++- .../utils/extensions/ParentTestExtensions.kt | 38 +++++++++++- 4 files changed, 113 insertions(+), 5 deletions(-) diff --git a/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/e2e/compose/CourseDetailsFrontPageE2ETest.kt b/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/e2e/compose/CourseDetailsFrontPageE2ETest.kt index 9bf77bcd40..f99da7f6ec 100644 --- a/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/e2e/compose/CourseDetailsFrontPageE2ETest.kt +++ b/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/e2e/compose/CourseDetailsFrontPageE2ETest.kt @@ -16,6 +16,7 @@ package com.instructure.parentapp.ui.e2e.compose import android.util.Log +import androidx.test.espresso.Espresso import com.instructure.canvas.espresso.FeatureCategory import com.instructure.canvas.espresso.Priority import com.instructure.canvas.espresso.SecondaryFeatureCategory @@ -32,6 +33,7 @@ import com.instructure.dataseeding.util.fromNow import com.instructure.dataseeding.util.iso8601 import com.instructure.parentapp.utils.ParentComposeTest import com.instructure.parentapp.utils.extensions.seedData +import com.instructure.parentapp.utils.extensions.seedDataForK5 import com.instructure.parentapp.utils.extensions.tokenLogin import dagger.hilt.android.testing.HiltAndroidTest import org.junit.Test @@ -76,7 +78,7 @@ class CourseDetailsFrontPageE2ETest : ParentComposeTest() { dashboardPage.waitForRender() coursesPage.clickCourseItem(course.name) - Log.d(STEP_TAG, "Navigate to Front Page Page by selecting Summary Tab.") + Log.d(STEP_TAG, "Navigate to Front Page Page by selecting Front Page Tab.") courseDetailsPage.selectTab("FRONT PAGE") Log.d(ASSERTION_TAG, "Assert that the 'FRONT PAGE' tab has been selected.") @@ -91,4 +93,58 @@ class CourseDetailsFrontPageE2ETest : ParentComposeTest() { Log.d(ASSERTION_TAG, "Assert that the Assignment Details Page is loaded successfully.") assignmentDetailsPage.assertAssignmentDetails(assignment) } + + @E2E + @Test + @TestMetaData(Priority.COMMON, FeatureCategory.COURSE_DETAILS, TestCategory.E2E, SecondaryFeatureCategory.FRONT_PAGE) + fun testFrontPageTabVisibilityForK5ElementaryCoursesE2E() { + + //Bug Ticket: MBL-19842 + Log.d(PREPARATION_TAG, "Seeding data for K5 sub-account: 2 homeroom courses. The parent observes the K5 elementary student.") + val data = seedDataForK5(students = 1, teachers = 1, parents = 1, homeroomCourses = 2) + val courseWithFrontPage = data.coursesList[0] + val courseWithoutFrontPage = data.coursesList[1] + val teacher = data.teachersList[0] + val parent = data.parentsList[0] + + val syllabusBody = "This is the syllabus body of the K5 elementary course." + val frontPageBody = "This is the front page body of the K5 elementary course." + + Log.d(PREPARATION_TAG, "Seed a front page for '${courseWithFrontPage.name}'.") + PagesApi.createCoursePage(courseWithFrontPage.id, teacher.token, frontPage = true, editingRoles = "public", body = frontPageBody) + + Log.d(PREPARATION_TAG, "Add a syllabus body to both courses so that both get SYLLABUS and SUMMARY tabs.") + CoursesApi.updateCourse(courseWithFrontPage.id, UpdateCourse(syllabusBody = syllabusBody)) + CoursesApi.updateCourse(courseWithoutFrontPage.id, UpdateCourse(syllabusBody = syllabusBody)) + + Log.d(STEP_TAG, "Login with user: '${parent.name}', login id: '${parent.loginId}'.") + tokenLogin(parent) + + Log.d(STEP_TAG, "Wait for the Dashboard Page to be rendered. Select '${courseWithFrontPage.name}' course.") + dashboardPage.waitForRender() + coursesPage.clickCourseItem(courseWithFrontPage.name) + + Log.d(ASSERTION_TAG, "Assert that '${courseWithFrontPage.name}' has 4 tabs: GRADES, FRONT PAGE, SYLLABUS, SUMMARY.") + courseDetailsPage.assertTabCount(4) + + Log.d(ASSERTION_TAG, "Assert that the 'FRONT PAGE' tab IS displayed for the course that has a front page, even though the course home is NOT set to 'wiki'.") + courseDetailsPage.assertTabDisplayed("FRONT PAGE") + + Log.d(STEP_TAG, "Navigate to the Front Page tab.") + courseDetailsPage.selectTab("FRONT PAGE") + + Log.d(ASSERTION_TAG, "Assert that the 'FRONT PAGE' tab has been selected and the front page body is displayed.") + courseDetailsPage.assertTabSelected("FRONT PAGE") + frontPagePage.assertFrontPageBody(frontPageBody) + + Log.d(STEP_TAG, "Navigate back to the Dashboard and select '${courseWithoutFrontPage.name}' course.") + Espresso.pressBack() + coursesPage.clickCourseItem(courseWithoutFrontPage.name) + + Log.d(ASSERTION_TAG, "Assert that '${courseWithoutFrontPage.name}' has 3 tabs: GRADES, SYLLABUS, SUMMARY — no FRONT PAGE tab since no front page was created.") + courseDetailsPage.assertTabCount(3) + + Log.d(ASSERTION_TAG, "Assert that the 'FRONT PAGE' tab is NOT displayed for the course that has no front page.") + courseDetailsPage.assertTabDoesNotExist("FRONT PAGE") + } } diff --git a/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/pages/classic/FrontPagePage.kt b/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/pages/classic/FrontPagePage.kt index 10c3ec2cb0..1253e65122 100644 --- a/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/pages/classic/FrontPagePage.kt +++ b/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/pages/classic/FrontPagePage.kt @@ -15,6 +15,7 @@ */ package com.instructure.parentapp.ui.pages.classic +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.web.assertion.WebViewAssertions import androidx.test.espresso.web.sugar.Web @@ -27,7 +28,7 @@ import org.hamcrest.Matchers class FrontPagePage : BasePage() { fun assertFrontPageBody(body: String) { - Web.onWebView(withId(R.id.contentWebView)) + Web.onWebView(Matchers.allOf(withId(R.id.contentWebView), isDisplayed())) .withElement(DriverAtoms.findElement(Locator.ID, "content")) .check( WebViewAssertions.webMatches( @@ -38,7 +39,7 @@ class FrontPagePage : BasePage() { } fun clickLink(linkId: String) { - Web.onWebView(withId(R.id.contentWebView)) + Web.onWebView(Matchers.allOf(withId(R.id.contentWebView), isDisplayed())) .withElement(DriverAtoms.findElement(Locator.ID, linkId)) .perform(DriverAtoms.webClick()) } diff --git a/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/pages/compose/CourseDetailsPage.kt b/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/pages/compose/CourseDetailsPage.kt index 31d32d4e8e..cb415f2d6d 100644 --- a/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/pages/compose/CourseDetailsPage.kt +++ b/apps/parent/src/androidTest/java/com/instructure/parentapp/ui/pages/compose/CourseDetailsPage.kt @@ -14,7 +14,6 @@ * limitations under the License. * */ - package com.instructure.parentapp.ui.pages.compose import androidx.compose.ui.graphics.Color @@ -27,6 +26,7 @@ import androidx.compose.ui.test.hasAnyDescendant import androidx.compose.ui.test.hasParent import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText +import androidx.compose.ui.test.isSelectable import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText @@ -61,6 +61,7 @@ class CourseDetailsPage(private val composeTestRule: ComposeTestRule) { fun selectTab(tabName: String) { composeTestRule.onNodeWithText(tabName).performClick() + composeTestRule.waitForIdle() } fun assertTabSelected(tabName: String) { @@ -80,8 +81,22 @@ class CourseDetailsPage(private val composeTestRule: ComposeTestRule) { composeTestRule.onNodeWithText(assignmentName).assertTextColor(Color(expectedTextColor)) } + fun assertTabDisplayed(tabName: String) { + composeTestRule.onNodeWithText(tabName).assertIsDisplayed() + } + + fun assertTabDoesNotExist(tabName: String) { + composeTestRule.onNodeWithText(tabName).assertDoesNotExist() + } + + fun assertTabCount(expectedCount: Int) { + composeTestRule.onAllNodes(hasAnyAncestor(hasTestTag("courseDetailsTabRow")) and isSelectable()) + .assertCountEquals(expectedCount) + } + fun clickComposeMessageFAB() { composeTestRule.onNodeWithContentDescription("Send a message about this course").performClick() + composeTestRule.waitForIdle() } fun assertHasAssignmentWithCheckpoints(assignmentName: String, dueAtString: String = "No due date", dueAtStringSecondCheckpoint: String? = null, expectedGrade: String? = null) { diff --git a/apps/parent/src/androidTest/java/com/instructure/parentapp/utils/extensions/ParentTestExtensions.kt b/apps/parent/src/androidTest/java/com/instructure/parentapp/utils/extensions/ParentTestExtensions.kt index 281d6beec0..dd9cb80a92 100644 --- a/apps/parent/src/androidTest/java/com/instructure/parentapp/utils/extensions/ParentTestExtensions.kt +++ b/apps/parent/src/androidTest/java/com/instructure/parentapp/utils/extensions/ParentTestExtensions.kt @@ -14,16 +14,19 @@ * along with this program. If not, see . * */ - package com.instructure.parentapp.utils.extensions import com.instructure.canvas.espresso.CanvasTest import com.instructure.canvasapi2.models.User +import com.instructure.dataseeding.api.EnrollmentsApi import com.instructure.dataseeding.api.SeedApi +import com.instructure.dataseeding.api.UserApi import com.instructure.dataseeding.model.CanvasUserApiModel import com.instructure.parentapp.features.login.LoginActivity import com.instructure.parentapp.utils.ParentTest +const val K5_SUB_ACCOUNT_ID = 181364L + fun ParentTest.tokenLogin(user: CanvasUserApiModel) { activityRule.runOnUiThread { @@ -91,3 +94,36 @@ fun seedData( ) return SeedApi.seedData(request) } + +fun seedDataForK5( + teachers: Int = 0, + courses: Int = 0, + students: Int = 0, + parents: Int = 0, + homeroomCourses: Int = 0, + announcements: Int = 0 +): SeedApi.SeededDataApiModel { + + val request = SeedApi.SeedDataRequest( + teachers = teachers, + students = students, + courses = courses, + homeroomCourses = homeroomCourses, + announcements = announcements, + accountId = K5_SUB_ACCOUNT_ID + ) + val data = SeedApi.seedDataForSubAccount(request) + + val observedStudents = data.studentsList.take(students) + repeat(parents) { + val parent = UserApi.createCanvasUser() + data.addParents(parent) + observedStudents.forEach { student -> + data.coursesList.forEach { course -> + data.addEnrollments(EnrollmentsApi.enrollUserAsObserver(course.id, parent.id, student.id)) + } + } + } + + return data +} From 3bc89532263af792f86857f9722ee1dfbb3bed07 Mon Sep 17 00:00:00 2001 From: "adam.nagy" Date: Thu, 16 Apr 2026 15:54:38 +0200 Subject: [PATCH 2/2] CR refs: MBL-19884 affects: Parent release note: --- .../parentapp/utils/extensions/ParentTestExtensions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/parent/src/androidTest/java/com/instructure/parentapp/utils/extensions/ParentTestExtensions.kt b/apps/parent/src/androidTest/java/com/instructure/parentapp/utils/extensions/ParentTestExtensions.kt index dd9cb80a92..121f28270a 100644 --- a/apps/parent/src/androidTest/java/com/instructure/parentapp/utils/extensions/ParentTestExtensions.kt +++ b/apps/parent/src/androidTest/java/com/instructure/parentapp/utils/extensions/ParentTestExtensions.kt @@ -25,7 +25,7 @@ import com.instructure.dataseeding.model.CanvasUserApiModel import com.instructure.parentapp.features.login.LoginActivity import com.instructure.parentapp.utils.ParentTest -const val K5_SUB_ACCOUNT_ID = 181364L +const val SUB_ACCOUNT_ID = 181364L fun ParentTest.tokenLogin(user: CanvasUserApiModel) { @@ -110,7 +110,7 @@ fun seedDataForK5( courses = courses, homeroomCourses = homeroomCourses, announcements = announcements, - accountId = K5_SUB_ACCOUNT_ID + accountId = SUB_ACCOUNT_ID ) val data = SeedApi.seedDataForSubAccount(request)