Skip to content

Commit df548b9

Browse files
committed
Merge branch 'master' into merge-release-parent
# Conflicts: # apps/parent/src/androidTest/java/com/instructure/parentapp/ui/e2e/compose/LoginE2ETest.kt
2 parents 66870a9 + 5dc7273 commit df548b9

56 files changed

Lines changed: 846 additions & 315 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: "Open Source Build: Parent"
2+
3+
on:
4+
schedule:
5+
- cron: '0 9 * * 1'
6+
workflow_dispatch:
7+
8+
jobs:
9+
build:
10+
name: Build Parent
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 1
17+
18+
- name: Run open source script
19+
run: ./open_source.sh
20+
21+
- name: Set up JDK 17
22+
uses: actions/setup-java@v4
23+
with:
24+
java-version: '17'
25+
distribution: 'temurin'
26+
27+
- name: Cache Gradle packages
28+
uses: actions/cache@v4
29+
with:
30+
path: |
31+
~/.gradle/caches
32+
~/.gradle/wrapper
33+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }}
34+
restore-keys: |
35+
${{ runner.os }}-gradle-
36+
37+
- name: Build DevDebug
38+
run: |
39+
./gradle/gradlew -p apps :parent:assembleDevDebug \
40+
--build-cache \
41+
--parallel \
42+
--max-workers=4 \
43+
--no-daemon \
44+
-Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError"
45+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: "Open Source Build: Student"
2+
3+
on:
4+
schedule:
5+
- cron: '0 9 * * 1'
6+
workflow_dispatch:
7+
8+
jobs:
9+
build:
10+
name: Build Student
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 1
17+
18+
- name: Run open source script
19+
run: ./open_source.sh
20+
21+
- name: Set up JDK 17
22+
uses: actions/setup-java@v4
23+
with:
24+
java-version: '17'
25+
distribution: 'temurin'
26+
27+
- name: Cache Gradle packages
28+
uses: actions/cache@v4
29+
with:
30+
path: |
31+
~/.gradle/caches
32+
~/.gradle/wrapper
33+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }}
34+
restore-keys: |
35+
${{ runner.os }}-gradle-
36+
37+
- name: Build DevDebug
38+
run: |
39+
./gradle/gradlew -p apps :student:assembleDevDebug \
40+
--build-cache \
41+
--parallel \
42+
--max-workers=4 \
43+
--no-daemon \
44+
-Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError"
45+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: "Open Source Build: Teacher"
2+
3+
on:
4+
schedule:
5+
- cron: '0 9 * * 1'
6+
workflow_dispatch:
7+
8+
jobs:
9+
build:
10+
name: Build Teacher
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 1
17+
18+
- name: Run open source script
19+
run: ./open_source.sh
20+
21+
- name: Set up JDK 17
22+
uses: actions/setup-java@v4
23+
with:
24+
java-version: '17'
25+
distribution: 'temurin'
26+
27+
- name: Cache Gradle packages
28+
uses: actions/cache@v4
29+
with:
30+
path: |
31+
~/.gradle/caches
32+
~/.gradle/wrapper
33+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }}
34+
restore-keys: |
35+
${{ runner.os }}-gradle-
36+
37+
- name: Build DevDebug
38+
run: |
39+
./gradle/gradlew -p apps :teacher:assembleDevDebug \
40+
--build-cache \
41+
--parallel \
42+
--max-workers=4 \
43+
--no-daemon \
44+
-Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError"
45+

README.md

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,22 @@ The open source code provided by the Android Team at Instructure.
66

77
## Building
88

9-
First, install the Flutter SDK using the instructions found [here](https://flutter.dev/docs/get-started/install).
10-
11-
Next, run `./open_source.sh` once. You may now use Gradle to build the apps.
9+
Run `./open_source.sh` once. You may now use Gradle to build the apps.
1210

1311
### Student, Teacher and native Parent
1412

1513
1. Open `apps/build.gradle` in Android Studio
16-
```
17-
Android Studio > Import Project > canvas-android/apps/build.gradle
18-
```
19-
20-
2. Select the app from the list of configurations (`student` or `teacher`)
21-
3. Tap 'Run' (`^R`) to run the app
22-
23-
### Flutter Parent
24-
25-
1. Open `canvas-android/apps/flutter_parent` in Android Studio.
26-
2. Make sure the `main.dart` configuration is selected
14+
```
15+
Android Studio > Import Project > canvas-android/apps/build.gradle
16+
```
17+
2. Select the app from the list of configurations
2718
3. Tap 'Run' (`^R`) to run the app
2819
29-
App | Command | Build Status
30-
--- | --- | ---
31-
Student | `./gradle/gradlew -p apps :student:assembleDevDebug` | [![Student build Status](https://app.bitrise.io/app/9417c28328c02b7c/status.svg?token=D7fHdeUlz19PurcEPIQNzw&branch=master)](https://app.bitrise.io/app/9417c28328c02b7c)
32-
Teacher | `./gradle/gradlew -p apps :teacher:assembleDevDebug` | [![Teacher build Status](https://app.bitrise.io/app/4f5339d0ec3436ca/status.svg?token=ATqaYNnYyS4eDUxc0d9EZQ&branch=master)](https://app.bitrise.io/app/4f5339d0ec3436ca)
33-
Parent | (in apps/flutter_parent) `flutter pub get; flutter build apk` | [![Parent build Status](https://app.bitrise.io/app/39fd3312f33be200/status.svg?token=jiiPeSZlSxrx5lkqccLN1Q&branch=master)](https://app.bitrise.io/app/39fd3312f33be200)
20+
| App | Command | Build Status |
21+
|---------|------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
22+
| Student | `./gradle/gradlew -p apps :student:assembleDevDebug` | [![Student](https://github.com/instructure/canvas-android/actions/workflows/open-source-build-student.yml/badge.svg)](https://github.com/instructure/canvas-android/actions/workflows/open-source-build-student.yml) |
23+
| Teacher | `./gradle/gradlew -p apps :teacher:assembleDevDebug` | [![Teacher](https://github.com/instructure/canvas-android/actions/workflows/open-source-build-teacher.yml/badge.svg)](https://github.com/instructure/canvas-android/actions/workflows/open-source-build-teacher.yml) |
24+
| Parent | `./gradle/gradlew -p apps :parent:assembleDevDebug` | [![Parent](https://github.com/instructure/canvas-android/actions/workflows/open-source-build-parent.yml/badge.svg)](https://github.com/instructure/canvas-android/actions/workflows/open-source-build-parent.yml) |
3425
3526
## Running tests
3627
@@ -42,11 +33,11 @@ To run unit tests for Student, Teacher and native Parent
4233
4334
#### The Applications we have published on Google Play.
4435
45-
App | Description
46-
--- | ---
47-
[Canvas Student][canvas] | Used by Students all over the world to be smarter, go faster, and do more.
48-
[Canvas Teacher][teacher] | User by Teachers all over the world to update course content or grade on the go.
49-
[Canvas Parent][parent] | Used by Parents all over the world to be parents.
36+
| App | Description |
37+
|---------------------------|----------------------------------------------------------------------------------|
38+
| [Canvas Student][canvas] | Used by Students all over the world to be smarter, go faster, and do more. |
39+
| [Canvas Teacher][teacher] | User by Teachers all over the world to update course content or grade on the go. |
40+
| [Canvas Parent][parent] | Used by Parents all over the world to be parents. |
5041
5142
[canvas]: https://play.google.com/store/apps/details?id=com.instructure.candroid
5243
[teacher]: https://play.google.com/store/apps/details?id=com.instructure.teacher
@@ -56,20 +47,20 @@ App | Description
5647
5748
#### These are things that we use internally to create our applications.
5849
59-
Module | Description
60-
--- | ---
61-
annotations | A wrapper for the PSPDFKit library and logic for annotation handling and converting in PDF documents.
62-
buildSrc | Library for common gradle dependencies and gradle transformers that are used by the project.
63-
canvas-api-2 | Canvas for Android Api used to talk to the Canvas LMS and is testable.
64-
dataseedingapi | gRPC wrapper for Canvas that enables creating data to test the apps.
65-
DocumentScanner | A wrapper for document scanning features.
66-
espresso | The UI testing library built on Espresso.
67-
interactions | Interactions for navigation used in the apps.
68-
login-api-2 | The libarary used to make logging in and getting a token relative easy and is testable.
69-
pandares | Collection of resources used in our apps.
70-
pandautils | The core library for the apps. All the common code is implemented here that is reused by the 3 apps.
71-
rceditor | A wrapper for rich content editing used in our apps.
72-
recyclerview | A fancy RecyclerView library that supports expanding and collapsing, pagination, and stuff like that. (deprecated)
50+
| Module | Description |
51+
|----------------|--------------------------------------------------------------------------------------------------------------------|
52+
| annotations | A wrapper for the PSPDFKit library and logic for annotation handling and converting in PDF documents. |
53+
| buildSrc | Library for common gradle dependencies and gradle transformers that are used by the project. |
54+
| canvas-api-2 | Canvas for Android Api used to talk to the Canvas LMS and is testable. |
55+
| dataseedingapi | gRPC wrapper for Canvas that enables creating data to test the apps. |
56+
| espresso | The UI testing library built on Espresso. |
57+
| horizon | Canvas Career experience for the Student app. |
58+
| interactions | Interactions for navigation used in the apps. |
59+
| login-api-2 | The libarary used to make logging in and getting a token relative easy and is testable. |
60+
| pandares | Collection of resources used in our apps. |
61+
| pandautils | The core library for the apps. All the common code is implemented here that is reused by the 3 apps. |
62+
| rceditor | A wrapper for rich content editing used in our apps. |
63+
| recyclerview | A fancy RecyclerView library that supports expanding and collapsing, pagination, and stuff like that. (deprecated) |
7364
7465
#### Our applications are licensed under the GPLv3 License.
7566

apps/parent/src/androidTest/java/com/instructure/parentapp/ui/e2e/compose/LoginE2ETest.kt

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -290,35 +290,6 @@ class LoginE2ETest : ParentComposeTest() {
290290
loginSignInPage.assertLoginEmailErrorMessage(NO_EMAIL_GIVEN_ERROR_MESSAGE) // Invalid credentials error message will be displayed within the email error message holder on the login page.
291291
}
292292

293-
private fun loginWithUser(user: CanvasUserApiModel, lastSchoolSaved: Boolean = false) {
294-
295-
Thread.sleep(5100) //Need to wait > 5 seconds before each login attempt because of new 'too many attempts' login policy on web.
296-
297-
if (lastSchoolSaved) {
298-
Log.d(STEP_TAG, "Click 'Find Another School' button.")
299-
loginLandingPage.clickFindAnotherSchoolButton()
300-
} else {
301-
Log.d(STEP_TAG, "Click 'Find My School' button.")
302-
loginLandingPage.clickFindMySchoolButton()
303-
}
304-
305-
Log.d(STEP_TAG, "Enter domain: '${user.domain}'.")
306-
loginFindSchoolPage.enterDomain(user.domain)
307-
308-
Log.d(STEP_TAG, "Click on 'Next' button on the Toolbar.")
309-
loginFindSchoolPage.clickToolbarNextMenuItem()
310-
loginSignInPage.loginAs(user)
311-
}
312-
313-
private fun loginWithLastSavedSchool(user: CanvasUserApiModel) {
314-
315-
Log.d(STEP_TAG, "Click on last saved school's button.")
316-
loginLandingPage.clickOnLastSavedSchoolButton()
317-
318-
Log.d(STEP_TAG, "Login with '${user.name}' user.")
319-
loginSignInPage.loginAs(user)
320-
}
321-
322293
@E2E
323294
@Test
324295
@TestMetaData(Priority.IMPORTANT, FeatureCategory.LOGIN, TestCategory.E2E)
@@ -405,7 +376,6 @@ class LoginE2ETest : ParentComposeTest() {
405376
}
406377

407378
@Test
408-
@Stub("Stubbed because there was some change on 7th or 8th of July, 2025 and on the CI it loads an invalid URL page, however the test runs locally.")
409379
@E2E
410380
@TestMetaData(Priority.IMPORTANT, FeatureCategory.LOGIN, TestCategory.E2E, SecondaryFeatureCategory.CANVAS_NETWORK)
411381
fun testCanvasNetworkSignInPageE2E() {
@@ -486,4 +456,32 @@ class LoginE2ETest : ParentComposeTest() {
486456
}
487457
}
488458

459+
private fun loginWithUser(user: CanvasUserApiModel, lastSchoolSaved: Boolean = false) {
460+
461+
Thread.sleep(5100) //Need to wait > 5 seconds before each login attempt because of new 'too many attempts' login policy on web.
462+
463+
if (lastSchoolSaved) {
464+
Log.d(STEP_TAG, "Click 'Find Another School' button.")
465+
loginLandingPage.clickFindAnotherSchoolButton()
466+
} else {
467+
Log.d(STEP_TAG, "Click 'Find My School' button.")
468+
loginLandingPage.clickFindMySchoolButton()
469+
}
470+
471+
Log.d(STEP_TAG, "Enter domain: '${user.domain}'.")
472+
loginFindSchoolPage.enterDomain(user.domain)
473+
474+
Log.d(STEP_TAG, "Click on 'Next' button on the Toolbar.")
475+
loginFindSchoolPage.clickToolbarNextMenuItem()
476+
loginSignInPage.loginAs(user)
477+
}
478+
479+
private fun loginWithLastSavedSchool(user: CanvasUserApiModel) {
480+
481+
Log.d(STEP_TAG, "Click on last saved school's button.")
482+
loginLandingPage.clickOnLastSavedSchoolButton()
483+
484+
Log.d(STEP_TAG, "Login with '${user.name}' user.")
485+
loginSignInPage.loginAs(user)
486+
}
489487
}

apps/parent/src/main/java/com/instructure/parentapp/features/inbox/list/ParentInboxRouter.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import androidx.fragment.app.FragmentActivity
2222
import com.instructure.canvasapi2.apis.InboxApi
2323
import com.instructure.canvasapi2.models.Attachment
2424
import com.instructure.canvasapi2.models.Conversation
25+
import com.instructure.canvasapi2.models.MediaComment
2526
import com.instructure.pandautils.features.inbox.list.InboxRouter
2627
import com.instructure.pandautils.features.inbox.utils.InboxComposeOptions
2728
import com.instructure.pandautils.utils.FileDownloader
@@ -63,6 +64,10 @@ class ParentInboxRouter(
6364
fileDownloader.downloadFileToDevice(attachment)
6465
}
6566

67+
override fun routeToMediaAttachment(mediaComment: MediaComment) {
68+
fileDownloader.downloadFileToDevice(mediaComment.url, mediaComment.displayName, mediaComment.contentType)
69+
}
70+
6671
override fun popDetailsScreen(activity: FragmentActivity?) {
6772
activity?.onBackPressed()
6873
}

apps/parent/src/test/java/com/instructure/parentapp/features/webview/SimpleWebViewRepositoryTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@ class SimpleWebViewRepositoryTest {
3939
fun `Get authenticated session successfully returns data`() = runTest {
4040
val expected = "sessionUrl"
4141

42-
coEvery { oAuthApi.getAuthenticatedSession(any(), any()) } returns DataResult.Success(AuthenticatedSession(sessionUrl = expected))
42+
coEvery { oAuthApi.getAuthenticatedSession(any(), any(), any()) } returns DataResult.Success(AuthenticatedSession(sessionUrl = expected))
4343

4444
val result = repository.getAuthenticatedSession("url")
4545
Assert.assertEquals(expected, result)
4646
}
4747

4848
@Test(expected = IllegalStateException::class)
4949
fun `Get authenticated session fails throws exception`() = runTest {
50-
coEvery { oAuthApi.getAuthenticatedSession(any(), any()) } returns DataResult.Fail()
50+
coEvery { oAuthApi.getAuthenticatedSession(any(), any(), any()) } returns DataResult.Fail()
5151

5252
repository.getAuthenticatedSession("url")
5353
}

apps/parent/src/test/java/com/instructure/parentapp/login/routevalidator/RouteValidatorViewModelTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class RouteValidatorViewModelTest {
153153
every { apiPrefs.getValidToken() } returns ""
154154
every { qrLogin.verifySSOLoginUri(any()) } returns true
155155
coEvery { qrLogin.performSSOLogin(any(), any(), any()) } returns OAuthTokenResponse()
156-
coEvery { oAuthApi.getAuthenticatedSession(any(), any()) } returns DataResult.Success(AuthenticatedSession("sessionUrl"))
156+
coEvery { oAuthApi.getAuthenticatedSession(any(), any(), any()) } returns DataResult.Success(AuthenticatedSession("sessionUrl"))
157157

158158
createViewModel()
159159

@@ -177,7 +177,7 @@ class RouteValidatorViewModelTest {
177177
realUser = TokenUser(1, "", ""),
178178
user = TokenUser(1, "", "")
179179
)
180-
coEvery { oAuthApi.getAuthenticatedSession(any(), any()) } returns DataResult.Success(AuthenticatedSession("sessionUrl"))
180+
coEvery { oAuthApi.getAuthenticatedSession(any(), any(), any()) } returns DataResult.Success(AuthenticatedSession("sessionUrl"))
181181

182182
createViewModel()
183183

apps/student/src/androidTest/java/com/instructure/student/ui/e2e/classic/LoginE2ETest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ class LoginE2ETest : StudentTest() {
394394
loginSignInPage.assertPageObjects()
395395
}
396396

397+
@E2E
397398
@Test
398399
fun testFindSchoolPageObjects() {
399400

@@ -404,13 +405,15 @@ class LoginE2ETest : StudentTest() {
404405
loginFindSchoolPage.assertPageObjects()
405406
}
406407

408+
@E2E
407409
@Test
408410
fun testLoginLandingPageObjects() {
409411

410412
Log.d(ASSERTION_TAG, "Assert that the Login Landing Page has been displayed.")
411413
loginLandingPage.assertPageObjects()
412414
}
413415

416+
@E2E
414417
@Test
415418
fun testLoginSignInPageObjects() {
416419

0 commit comments

Comments
 (0)