From ea5cdac728263fdc48b480460f3362a7f5fe221d Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Wed, 25 Mar 2026 18:18:17 +0000 Subject: test(ci): share APKs between jobs and expand smoke tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI — build job now uploads both APKs as the 'test-apks' artifact. smoke-test job downloads them and passes -x assembleDebug -x assembleDebugAndroidTest to skip recompilation (~4 min saved). Test results uploaded as 'smoke-test-results' artifact on every run. Smoke tests expanded from 1 → 11 tests covering: - MainActivity launches without crash - All 4 bottom-nav tabs are displayed - Safety tab: Safety Dashboard, ACTIVATE MOB, ANCHOR WATCH visible - Log tab: voice-log mic FAB visible - Instruments tab: bottom sheet displayed - Map tab: returns from overlay, mapView visible - MOB FAB: always visible, visible on Safety tab - Record Track FAB: displayed, toggles to Stop Recording, toggles back MainActivity: moved isRecording observer to initializeUI() so the FAB content description updates without requiring GPS permission (needed for emulator tests that run without location permission). Co-Authored-By: Claude Sonnet 4.6 --- .../kotlin/org/terst/nav/MainActivitySmokeTest.kt | 110 +++++++++++++++++++-- 1 file changed, 103 insertions(+), 7 deletions(-) (limited to 'android-app/app/src/androidTest/kotlin/org/terst') diff --git a/android-app/app/src/androidTest/kotlin/org/terst/nav/MainActivitySmokeTest.kt b/android-app/app/src/androidTest/kotlin/org/terst/nav/MainActivitySmokeTest.kt index 0824abe..a13ef7f 100644 --- a/android-app/app/src/androidTest/kotlin/org/terst/nav/MainActivitySmokeTest.kt +++ b/android-app/app/src/androidTest/kotlin/org/terst/nav/MainActivitySmokeTest.kt @@ -1,18 +1,24 @@ package org.terst.nav import androidx.test.core.app.ActivityScenario +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withContentDescription +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Before import org.junit.Test import org.junit.runner.RunWith /** - * Smoke test: verifies MainActivity launches without crashing. + * Smoke tests: verify the main UI surfaces launch and respond correctly. + * These run on an emulator without GPS permission, so no LocationService. * - * Run on an emulator/device via: - * ./gradlew connectedDebugAndroidTest - * - * In CI, requires an emulator step before the Gradle task. + * Run locally: ./gradlew connectedDebugAndroidTest + * In CI: smoke-test job via android-emulator-runner */ @RunWith(AndroidJUnit4::class) class MainActivitySmokeTest { @@ -22,14 +28,104 @@ class MainActivitySmokeTest { NavApplication.isTesting = true } + // ── Launch ───────────────────────────────────────────────────────────── + @Test fun mainActivity_launches_withoutCrash() { ActivityScenario.launch(MainActivity::class.java).use { scenario -> - // If we reach this line the activity started without throwing. - // onActivity lets us assert it is in a resumed state. scenario.onActivity { activity -> assert(!activity.isFinishing) { "MainActivity finished immediately after launch" } } } } + + // ── Bottom nav ───────────────────────────────────────────────────────── + + @Test + fun bottomNav_allFourTabs_areDisplayed() { + ActivityScenario.launch(MainActivity::class.java).use { + onView(withText("Map")).check(matches(isDisplayed())) + onView(withText("Instruments")).check(matches(isDisplayed())) + onView(withText("Log")).check(matches(isDisplayed())) + onView(withText("Safety")).check(matches(isDisplayed())) + } + } + + @Test + fun bottomNav_safetyTab_showsSafetyDashboard() { + ActivityScenario.launch(MainActivity::class.java).use { + onView(withText("Safety")).perform(click()) + onView(withText("Safety Dashboard")).check(matches(isDisplayed())) + onView(withText("ACTIVATE MOB")).check(matches(isDisplayed())) + onView(withText("ANCHOR WATCH")).check(matches(isDisplayed())) + } + } + + @Test + fun bottomNav_logTab_showsVoiceLogUi() { + ActivityScenario.launch(MainActivity::class.java).use { + onView(withText("Log")).perform(click()) + onView(withContentDescription("Start voice recognition")).check(matches(isDisplayed())) + } + } + + @Test + fun bottomNav_instrumentsTab_isSelectable() { + ActivityScenario.launch(MainActivity::class.java).use { + onView(withText("Instruments")).perform(click()) + onView(withId(R.id.instrument_bottom_sheet)).check(matches(isDisplayed())) + } + } + + @Test + fun bottomNav_mapTab_returnsFromOverlay() { + ActivityScenario.launch(MainActivity::class.java).use { + onView(withText("Safety")).perform(click()) + onView(withText("Map")).perform(click()) + onView(withId(R.id.mapView)).check(matches(isDisplayed())) + } + } + + // ── Persistent FABs ──────────────────────────────────────────────────── + + @Test + fun fabMob_isAlwaysVisible() { + ActivityScenario.launch(MainActivity::class.java).use { + onView(withContentDescription("Man Overboard")).check(matches(isDisplayed())) + } + } + + @Test + fun fabMob_remainsVisibleOnSafetyTab() { + ActivityScenario.launch(MainActivity::class.java).use { + onView(withText("Safety")).perform(click()) + onView(withContentDescription("Man Overboard")).check(matches(isDisplayed())) + } + } + + // ── Track recording ──────────────────────────────────────────────────── + + @Test + fun fabRecordTrack_isDisplayedWithRecordDescription() { + ActivityScenario.launch(MainActivity::class.java).use { + onView(withContentDescription("Record Track")).check(matches(isDisplayed())) + } + } + + @Test + fun fabRecordTrack_togglesToStopRecording_onFirstClick() { + ActivityScenario.launch(MainActivity::class.java).use { + onView(withContentDescription("Record Track")).perform(click()) + onView(withContentDescription("Stop Recording")).check(matches(isDisplayed())) + } + } + + @Test + fun fabRecordTrack_togglesBackToRecord_onSecondClick() { + ActivityScenario.launch(MainActivity::class.java).use { + onView(withContentDescription("Record Track")).perform(click()) + onView(withContentDescription("Stop Recording")).perform(click()) + onView(withContentDescription("Record Track")).check(matches(isDisplayed())) + } + } } -- cgit v1.2.3