diff --git a/app/unit-tests/src/org/commcare/l10n/BaseConnectScreenshotTest.kt b/app/unit-tests/src/org/commcare/l10n/BaseConnectScreenshotTest.kt index a4bfb746f1..340d4ad339 100644 --- a/app/unit-tests/src/org/commcare/l10n/BaseConnectScreenshotTest.kt +++ b/app/unit-tests/src/org/commcare/l10n/BaseConnectScreenshotTest.kt @@ -29,7 +29,7 @@ import org.junit.rules.TestRule /** * The showcase user signed in to Connect, whose screens read seeded data from the local database, with every - * network call answered empty so none of it is pruned. + * network call answered by [responseBody], empty unless a test says otherwise, so none of it is pruned. */ abstract class BaseConnectScreenshotTest( protected val target: ScreenshotTarget, @@ -37,19 +37,19 @@ abstract class BaseConnectScreenshotTest( @get:Rule val localeRule: TestRule = L10nScreenshots.localeRule(target) - private val mockApi = ConnectMockApiServer() + protected val mockApi = ConnectMockApiServer() @Before fun setUpConnect() { mockApi.start() - mockApi.server.dispatcher = emptyJsonDispatcher() + mockApi.server.dispatcher = showcaseDispatcher() ConnectRepository.resetInstance() ConnectRequestManager.cancelAll() mockOutsideWorld() ConnectTestUtils.createConnectDbFile() ConnectUserDatabaseUtil.storeUser( - ConnectScreenshotData.user(hasConnectAccess = true).apply { + ConnectScreenshotData.buildUser(hasConnectAccess = true).apply { updateConnectToken("showcase-connect-token", ConnectTestUtils.daysFromNow(1)) }, ) @@ -86,8 +86,12 @@ abstract class BaseConnectScreenshotTest( every { AppUtils.isAppInstalled(any()) } returns true } - private fun emptyJsonDispatcher(): Dispatcher = + /** The JSON body the mock Connect server answers a request for [path] with. */ + protected open fun responseBody(path: String): String = "{}" + + private fun showcaseDispatcher(): Dispatcher = object : Dispatcher() { - override fun dispatch(request: RecordedRequest): MockResponse = MockResponse().setResponseCode(200).setBody("{}") + override fun dispatch(request: RecordedRequest): MockResponse = + MockResponse().setResponseCode(200).setBody(responseBody(request.requestUrl?.encodedPath.orEmpty())) } } diff --git a/app/unit-tests/src/org/commcare/l10n/ChannelListScreenshotTest.kt b/app/unit-tests/src/org/commcare/l10n/ChannelListScreenshotTest.kt index fb08a18470..55557ad508 100644 --- a/app/unit-tests/src/org/commcare/l10n/ChannelListScreenshotTest.kt +++ b/app/unit-tests/src/org/commcare/l10n/ChannelListScreenshotTest.kt @@ -21,8 +21,8 @@ class ChannelListScreenshotTest( @Before fun seedMessaging() { val context = ApplicationProvider.getApplicationContext() - ConnectScreenshotData.channels().forEach { ConnectMessagingDatabaseHelper.storeMessagingChannel(context, it) } - ConnectScreenshotData.messages().forEach { ConnectMessagingDatabaseHelper.storeMessagingMessage(context, it) } + ConnectScreenshotData.buildChannels().forEach { ConnectMessagingDatabaseHelper.storeMessagingChannel(context, it) } + ConnectScreenshotData.buildMessages().forEach { ConnectMessagingDatabaseHelper.storeMessagingMessage(context, it) } } @Test diff --git a/app/unit-tests/src/org/commcare/l10n/ChannelMessagesScreenshotTest.kt b/app/unit-tests/src/org/commcare/l10n/ChannelMessagesScreenshotTest.kt index f8b2b62068..9783432cb3 100644 --- a/app/unit-tests/src/org/commcare/l10n/ChannelMessagesScreenshotTest.kt +++ b/app/unit-tests/src/org/commcare/l10n/ChannelMessagesScreenshotTest.kt @@ -25,8 +25,8 @@ class ChannelMessagesScreenshotTest( @Before fun seedMessaging() { val context = ApplicationProvider.getApplicationContext() - ConnectScreenshotData.channels().forEach { ConnectMessagingDatabaseHelper.storeMessagingChannel(context, it) } - ConnectScreenshotData.messages().forEach { ConnectMessagingDatabaseHelper.storeMessagingMessage(context, it) } + ConnectScreenshotData.buildChannels().forEach { ConnectMessagingDatabaseHelper.storeMessagingChannel(context, it) } + ConnectScreenshotData.buildMessages().forEach { ConnectMessagingDatabaseHelper.storeMessagingMessage(context, it) } } @Test diff --git a/app/unit-tests/src/org/commcare/l10n/ConnectScreenshotData.kt b/app/unit-tests/src/org/commcare/l10n/ConnectScreenshotData.kt index 33743b4393..7b55a20705 100644 --- a/app/unit-tests/src/org/commcare/l10n/ConnectScreenshotData.kt +++ b/app/unit-tests/src/org/commcare/l10n/ConnectScreenshotData.kt @@ -20,11 +20,20 @@ object ConnectScreenshotData { private const val ORGANIZATION = "Kisumu Community Health Partners" private const val CURRENCY = "INR" private const val LEARN_MODULES = 5 + private const val LEARNING_MODULES_DONE = 3 private const val MAX_VISITS = 40 + private const val BUDGET_PER_VISIT = 1250 + private val HOUSEHOLDS = listOf("Achieng Odhiambo", "Baraka Mwangi", "Wanjiku Kamau", "Otieno Family", "Nyambura Njoroge") + private const val DELIVERY_VISITS = 27 + private const val VISITS_PER_DAY = 4 + private const val PAYMENT_UNIT_ID = "household-visit" const val CONVERSATION_CHANNEL_ID = "showcase-channel-partners" + const val LEARNING_OPPORTUNITY_ID = 1 + const val DELIVERING_OPPORTUNITY_ID = 2 + const val NEW_OPPORTUNITY_ID = 4 /** A PersonalID user with a full name, a Kenyan phone number and an email. */ - fun user(hasConnectAccess: Boolean = false): ConnectUserRecord = + fun buildUser(hasConnectAccess: Boolean = false): ConnectUserRecord = ConnectUserRecord( "+254712345678", "showcase-user-id", @@ -41,57 +50,144 @@ object ConnectScreenshotData { } /** One opportunity per state the opportunity list shows, with a long title and an expiry warning among them. */ - fun opportunities(): List = + fun buildOpportunities(): List = listOf( - opportunity(1, "Household Malaria Prevention Survey", ConnectJobRecord.STATUS_LEARNING, endInDays = 21, modulesDone = 3), - opportunity( - 2, + buildOpportunity( + LEARNING_OPPORTUNITY_ID, + "Household Malaria Prevention Survey", + ConnectJobRecord.STATUS_LEARNING, + endInDays = 21, + modulesDone = LEARNING_MODULES_DONE, + ), + buildOpportunity( + DELIVERING_OPPORTUNITY_ID, "Child Growth Monitoring and Nutrition Counselling, Kisumu County", ConnectJobRecord.STATUS_DELIVERING, endInDays = 2, - visitsDone = 27, + visitsDone = DELIVERY_VISITS, + ), + buildOpportunity(3, "Maternal Health Home Visits", ConnectJobRecord.STATUS_DELIVERING, endInDays = 60, visitsDone = 12), + buildOpportunity( + NEW_OPPORTUNITY_ID, + "Community Vaccination Awareness Campaign", + ConnectJobRecord.STATUS_AVAILABLE_NEW, + endInDays = 45, ), - opportunity(3, "Maternal Health Home Visits", ConnectJobRecord.STATUS_DELIVERING, endInDays = 60, visitsDone = 12), - opportunity(4, "Community Vaccination Awareness Campaign", ConnectJobRecord.STATUS_AVAILABLE_NEW, endInDays = 45), - opportunity(5, "Polio Campaign Follow-up", ConnectJobRecord.STATUS_DELIVERING, endInDays = -14, visitsDone = MAX_VISITS), - opportunity(6, "Bed Net Distribution Check", ConnectJobRecord.STATUS_DELIVERING, endInDays = -40, visitsDone = 18), + buildOpportunity(5, "Polio Campaign Follow-up", ConnectJobRecord.STATUS_DELIVERING, endInDays = -14, visitsDone = MAX_VISITS), + buildOpportunity(6, "Bed Net Distribution Check", ConnectJobRecord.STATUS_DELIVERING, endInDays = -40, visitsDone = 18), ) + /** The opportunity UUID the server and the local database key the opportunity with [id] by. */ + fun getOpportunityUuid(id: Int): String = "showcase-opportunity-$id" + + /** + * The learn progress response for the opportunity with [id]: the learning opportunity is part way through its + * modules, and every other one has finished them and passed the assessment. + */ + fun buildLearningProgressJson(id: Int): String { + val modulesDone = if (id == LEARNING_OPPORTUNITY_ID) LEARNING_MODULES_DONE else LEARN_MODULES + return JSONObject() + .put( + "completed_modules", + JSONArray().apply { + (1..modulesDone).forEach { module -> + put( + JSONObject().apply { + put("id", module) + put("date", formatIsoDateTime(minutesAgo = (40 - module * 6) * 24 * 60)) + put("module", module) + put("duration", "01:45:00") + }, + ) + } + }, + ).put( + "assessments", + JSONArray().apply { + if (modulesDone == LEARN_MODULES) { + put( + JSONObject().apply { + put("date", formatIsoDateTime(minutesAgo = 9 * 24 * 60)) + put("score", 92) + put("passing_score", 80) + put("passed", true) + }, + ) + } + }, + ).toString() + } + + /** + * The delivery progress response for the delivering opportunity: its visits spread over the last week, the newest + * of them still pending and one rejected, and two confirmed payments ahead of one still to confirm. + */ + fun buildDeliveryProgressJson(): String { + val rejectedVisit = 7 + val pendingVisits = 2 + val approvedVisits = DELIVERY_VISITS - pendingVisits - 1 + return JSONObject() + .put("max_payments", MAX_VISITS) + .put("payment_accrued", approvedVisits * BUDGET_PER_VISIT) + .put("is_user_suspended", false) + .put( + "deliveries", + JSONArray().apply { + (0 until DELIVERY_VISITS).forEach { visit -> + val status = + when { + visit < pendingVisits -> "pending" + visit == rejectedVisit -> "rejected" + else -> "approved" + } + put(buildDeliveryJson(visit, status, minutesAgo = (visit / VISITS_PER_DAY) * 24 * 60 + 30 + visit * 20)) + } + }, + ).put( + "payments", + JSONArray().apply { + put(buildPaymentJson("1", visits = 12, daysAgo = 14, confirmed = true)) + put(buildPaymentJson("2", visits = 6, daysAgo = 7, confirmed = true)) + put(buildPaymentJson("3", visits = 5, daysAgo = 1, confirmed = false)) + }, + ).toString() + } + /** Subscribed channels with unread, read and sent last messages, plus one the worker has unsubscribed from. */ - fun channels(): List = + fun buildChannels(): List = listOf( - channel(CONVERSATION_CHANNEL_ID, ORGANIZATION, consented = true), - channel("showcase-channel-support", "Connect Support", consented = true), - channel("showcase-channel-malaria", "Household Malaria Prevention Survey", consented = true), - channel("showcase-channel-nutrition", "Nutrition Programme Updates", consented = false), + buildChannel(CONVERSATION_CHANNEL_ID, ORGANIZATION, consented = true), + buildChannel("showcase-channel-support", "Connect Support", consented = true), + buildChannel("showcase-channel-malaria", "Household Malaria Prevention Survey", consented = true), + buildChannel("showcase-channel-nutrition", "Nutrition Programme Updates", consented = false), ) /** A conversation in [CONVERSATION_CHANNEL_ID] ending in two unread messages, and a last message in each other subscribed channel. */ - fun messages(): List = + fun buildMessages(): List = listOf( - message( + buildMessage( CONVERSATION_CHANNEL_ID, 1, minutesAgo = 26 * 60, outgoing = false, "Welcome to the Maternal Health Home Visits programme! Reply here if you have any questions.", ), - message( + buildMessage( CONVERSATION_CHANNEL_ID, 2, minutesAgo = 25 * 60, outgoing = true, "Thank you. Do I need to visit every household on my list this week?", ), - message( + buildMessage( CONVERSATION_CHANNEL_ID, 3, minutesAgo = 24 * 60, outgoing = false, "No, aim for 8 visits a day. Start with the households marked as high priority, and record each visit before you leave the house.", ), - message(CONVERSATION_CHANNEL_ID, 4, minutesAgo = 50, outgoing = true, "Understood. I finished 6 visits today."), - message( + buildMessage(CONVERSATION_CHANNEL_ID, 4, minutesAgo = 50, outgoing = true, "Understood. I finished 6 visits today."), + buildMessage( CONVERSATION_CHANNEL_ID, 5, minutesAgo = 12, @@ -99,7 +195,7 @@ object ConnectScreenshotData { "Great work! Your payment for last week has been approved.", viewed = false, ), - message( + buildMessage( CONVERSATION_CHANNEL_ID, 6, minutesAgo = 10, @@ -107,12 +203,24 @@ object ConnectScreenshotData { "Remember that the programme ends on Friday.", viewed = false, ), - message("showcase-channel-support", 1, minutesAgo = 30 * 60, outgoing = true, "My photo upload keeps failing, can you help?"), - message("showcase-channel-malaria", 1, minutesAgo = 6 * 24 * 60, outgoing = false, "The training modules are now available."), + buildMessage( + "showcase-channel-support", + 1, + minutesAgo = 30 * 60, + outgoing = true, + "My photo upload keeps failing, can you help?", + ), + buildMessage( + "showcase-channel-malaria", + 1, + minutesAgo = 6 * 24 * 60, + outgoing = false, + "The training modules are now available.", + ), ) /** The credentials response for Work History: one entry per activity level, newest first once sorted. */ - fun workHistoryResponse(): String = + fun buildWorkHistoryJson(): String = JSONObject() .put( "credentials", @@ -141,7 +249,7 @@ object ConnectScreenshotData { }, ).toString() - private fun channel( + private fun buildChannel( channelId: String, name: String, consented: Boolean, @@ -160,7 +268,7 @@ object ConnectScreenshotData { key = "showcase-key" } - private fun message( + private fun buildMessage( channelId: String, index: Int, minutesAgo: Int, @@ -178,7 +286,7 @@ object ConnectScreenshotData { userViewed = viewed } - private fun opportunity( + private fun buildOpportunity( id: Int, title: String, status: Int, @@ -186,11 +294,11 @@ object ConnectScreenshotData { modulesDone: Int = LEARN_MODULES, visitsDone: Int = 0, ): ConnectJobRecord = - ConnectJobRecord.fromJson(jobJson(id, title, endInDays, modulesDone, visitsDone)).apply { + ConnectJobRecord.fromJson(buildJobJson(id, title, endInDays, modulesDone, visitsDone)).apply { this.status = status } - private fun jobJson( + private fun buildJobJson( id: Int, title: String, endInDays: Int, @@ -199,20 +307,20 @@ object ConnectScreenshotData { ): JSONObject = JSONObject().apply { put("id", id) - put("opportunity_id", "showcase-opportunity-$id") + put("opportunity_id", getOpportunityUuid(id)) put("name", title) put("description", "Visit households in your area, record what you find and share health guidance with each family.") put("short_description", "Visit households and record what you find.") put("organization", ORGANIZATION) - put("start_date", isoDate(endInDays - 120)) - put("end_date", isoDate(endInDays)) + put("start_date", formatIsoDate(endInDays - 120)) + put("end_date", formatIsoDate(endInDays)) put("max_visits_per_user", MAX_VISITS) put("daily_max_visits_per_user", 8) - put("budget_per_visit", 1250) - put("budget_per_user", 1250 * MAX_VISITS) + put("budget_per_visit", BUDGET_PER_VISIT) + put("budget_per_user", BUDGET_PER_VISIT * MAX_VISITS) put("currency", CURRENCY) put("deliver_progress", visitsDone) - put("payment_units", paymentUnitsJson()) + put("payment_units", buildPaymentUnitsJson()) put( "learn_progress", JSONObject().apply { @@ -220,23 +328,57 @@ object ConnectScreenshotData { put("completed_modules", modulesDone) }, ) - put("learn_app", appJson("showcase-learn-$id", withModules = true)) - put("deliver_app", appJson("showcase-deliver-$id", withModules = false)) + put("learn_app", buildAppJson("showcase-learn-$id", withModules = true)) + put("deliver_app", buildAppJson("showcase-deliver-$id", withModules = false)) } - private fun paymentUnitsJson(): JSONArray = + private fun buildPaymentUnitsJson(): JSONArray = JSONArray().put( JSONObject().apply { put("id", 1) - put("payment_unit_id", "household-visit") + put("payment_unit_id", PAYMENT_UNIT_ID) put("name", "Household visit") put("max_total", MAX_VISITS) put("max_daily", 8) - put("amount", 1250) + put("amount", BUDGET_PER_VISIT) }, ) - private fun appJson( + private fun buildDeliveryJson( + visit: Int, + status: String, + minutesAgo: Int, + ): JSONObject = + JSONObject().apply { + put("id", visit + 1) + put("visit_date", formatIsoDateTime(minutesAgo)) + put("status", status) + put("deliver_unit_name", "Household visit") + put("deliver_unit_slug", PAYMENT_UNIT_ID) + put("deliver_unit_slug_id", PAYMENT_UNIT_ID) + put("entity_id", "showcase-household-$visit") + put("entity_name", HOUSEHOLDS[visit % HOUSEHOLDS.size]) + put("reason", if (status == "rejected") "The photo of the growth chart is missing." else "") + } + + private fun buildPaymentJson( + id: String, + visits: Int, + daysAgo: Int, + confirmed: Boolean, + ): JSONObject = + JSONObject().apply { + put("id", id) + put("payment_id", "showcase-payment-$id") + put("date_paid", formatIsoDateTime(minutesAgo = daysAgo * 24 * 60)) + put("amount", visits * BUDGET_PER_VISIT) + put("confirmed", confirmed) + if (confirmed) { + put("date_confirmed", formatIsoDate(-daysAgo + 1)) + } + } + + private fun buildAppJson( appId: String, withModules: Boolean, ): JSONObject = @@ -248,10 +390,10 @@ object ConnectScreenshotData { put("organization", ORGANIZATION) put("passing_score", 80) put("install_url", "https://example.org/$appId.ccz") - put("learn_modules", if (withModules) modulesJson() else JSONArray()) + put("learn_modules", if (withModules) buildModulesJson() else JSONArray()) } - private fun modulesJson(): JSONArray = + private fun buildModulesJson(): JSONArray = JSONArray().apply { listOf("Introduction to Connect", "Talking with Households", "Recognising Danger Signs", "Recording a Visit", "Referrals") .forEachIndexed { index, name -> @@ -267,6 +409,10 @@ object ConnectScreenshotData { } } - private fun isoDate(daysFromNow: Int): String = + private fun formatIsoDateTime(minutesAgo: Int): String = + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US) + .format(Date(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(minutesAgo.toLong()))) + + private fun formatIsoDate(daysFromNow: Int): String = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Calendar.getInstance().apply { add(Calendar.DAY_OF_YEAR, daysFromNow) }.time) } diff --git a/app/unit-tests/src/org/commcare/l10n/L10nScreenshots.kt b/app/unit-tests/src/org/commcare/l10n/L10nScreenshots.kt index c29b9b0a8b..abba7474be 100644 --- a/app/unit-tests/src/org/commcare/l10n/L10nScreenshots.kt +++ b/app/unit-tests/src/org/commcare/l10n/L10nScreenshots.kt @@ -1,7 +1,9 @@ package org.commcare.l10n import android.app.Activity +import com.github.takahirom.roborazzi.ExperimentalRoborazziApi import com.github.takahirom.roborazzi.captureRoboImage +import com.github.takahirom.roborazzi.captureScreenRoboImage import org.junit.Assume.assumeTrue import org.junit.rules.TestRule import org.junit.runner.Description @@ -104,12 +106,27 @@ object L10nScreenshots { ) { controller.visible() ShadowLooper.idleMainLooper() - val file = File(File(outDir, target.folder), "$screen.png") - file.parentFile?.mkdirs() controller .get() .window .decorView - .captureRoboImage(file.absolutePath) + .captureRoboImage(outputFile(target, screen).absolutePath) } + + /** Records every window on screen, so a dialog or bottom sheet shows over the activity beneath it. */ + @OptIn(ExperimentalRoborazziApi::class) + fun captureScreen( + controller: ActivityController, + target: ScreenshotTarget, + screen: String, + ) { + controller.visible() + ShadowLooper.idleMainLooper() + captureScreenRoboImage(outputFile(target, screen).absolutePath) + } + + private fun outputFile( + target: ScreenshotTarget, + screen: String, + ): File = File(File(outDir, target.folder), "$screen.png").also { it.parentFile?.mkdirs() } } diff --git a/app/unit-tests/src/org/commcare/l10n/OpportunityHomeScreenshotTest.kt b/app/unit-tests/src/org/commcare/l10n/OpportunityHomeScreenshotTest.kt new file mode 100644 index 0000000000..afee93dfa7 --- /dev/null +++ b/app/unit-tests/src/org/commcare/l10n/OpportunityHomeScreenshotTest.kt @@ -0,0 +1,143 @@ +package org.commcare.l10n + +import android.content.Intent +import androidx.navigation.fragment.NavHostFragment +import androidx.test.core.app.ApplicationProvider +import androidx.viewpager2.widget.ViewPager2 +import org.commcare.CommCareTestApplication +import org.commcare.activities.connect.ConnectActivity +import org.commcare.connect.ConnectConstants +import org.commcare.connect.ConnectOpportunityHomeFragmentDirections +import org.commcare.connect.database.ConnectJobUtils +import org.commcare.connect.opportunityHomeSurface +import org.commcare.connect.repository.ConnectRepository +import org.commcare.connect.repository.ConnectSyncPreferences +import org.commcare.dalvik.R +import org.commcare.fragments.connect.ConnectDeliveryHomeFragment +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.ParameterizedRobolectricTestRunner +import org.robolectric.Robolectric +import org.robolectric.android.controller.ActivityController +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.shadows.ShadowLooper + +@Config(application = CommCareTestApplication::class, sdk = [34]) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@RunWith(ParameterizedRobolectricTestRunner::class) +class OpportunityHomeScreenshotTest( + target: ScreenshotTarget, +) : BaseConnectScreenshotTest(target) { + @Before + fun seedOpportunities() { + ConnectJobUtils.storeJobs(ApplicationProvider.getApplicationContext(), ConnectScreenshotData.buildOpportunities(), true) + ConnectSyncPreferences.getInstance().apply { + markSessionStart() + storeLastSyncTime(ConnectRepository.SYNC_KEY_OPPORTUNITIES) + } + } + + override fun responseBody(path: String): String { + val opportunityId = + OPPORTUNITY_PATH + .find(path) + ?.groupValues + ?.get(1) + ?.toIntOrNull() ?: return "{}" + return when { + path.endsWith("/learn_progress") -> { + ConnectScreenshotData.buildLearningProgressJson(opportunityId) + } + + path.endsWith("/delivery_progress") && opportunityId == ConnectScreenshotData.DELIVERING_OPPORTUNITY_ID -> { + ConnectScreenshotData.buildDeliveryProgressJson() + } + + else -> { + "{}" + } + } + } + + @Test + fun jobIntro() { + val controller = openOpportunityHome(ConnectScreenshotData.NEW_OPPORTUNITY_ID) + L10nScreenshots.capture(controller, target, "21_connect_job_intro") + } + + @Test + fun learnModulesSheet() { + val controller = openOpportunityHome(ConnectScreenshotData.NEW_OPPORTUNITY_ID) + navHostOf(controller).navController.navigate( + ConnectOpportunityHomeFragmentDirections.actionConnectOpportunityHomeFragmentToConnectLearnModulesBottomSheet(), + ) + ShadowLooper.idleMainLooper() + L10nScreenshots.captureScreen(controller, target, "22_connect_learn_modules_sheet") + } + + @Test + fun learningProgress() { + val controller = openOpportunityHome(ConnectScreenshotData.LEARNING_OPPORTUNITY_ID) + mockApi.awaitRequest() + L10nScreenshots.capture(controller, target, "23_connect_learning_progress") + } + + @Test + fun deliveryDashboard() = captureDeliveryTab(ConnectDeliveryHomeFragment.TAB_DASHBOARD, "24_connect_delivery_dashboard") + + @Test + fun deliveryPayment() = captureDeliveryTab(ConnectDeliveryHomeFragment.TAB_PAYMENT, "25_connect_delivery_payment") + + @Test + fun deliveryVisits() = captureDeliveryTab(ConnectDeliveryHomeFragment.TAB_VISITS, "26_connect_delivery_visits") + + @Test + fun deliveryMore() = captureDeliveryTab(ConnectDeliveryHomeFragment.TAB_MORE, "27_connect_delivery_more") + + /** Delivery progress syncs first, and its success fetches the learn records the More tab reads. */ + private fun captureDeliveryTab( + tab: Int, + screen: String, + ) { + val controller = openOpportunityHome(ConnectScreenshotData.DELIVERING_OPPORTUNITY_ID) + mockApi.awaitRequest() + mockApi.awaitRequest() + navHostOf(controller) + .opportunityHomeSurface() + .requireView() + .findViewById(R.id.connect_delivery_home_view_pager) + .setCurrentItem(tab, false) + ShadowLooper.idleMainLooper() + L10nScreenshots.capture(controller, target, screen) + } + + private fun openOpportunityHome(opportunityId: Int): ActivityController { + val intent = + Intent(ApplicationProvider.getApplicationContext(), ConnectActivity::class.java).apply { + putExtra(ConnectConstants.GO_TO_JOB_STATUS, true) + putExtra(ConnectConstants.OPPORTUNITY_UUID, ConnectScreenshotData.getOpportunityUuid(opportunityId)) + } + val controller = + Robolectric + .buildActivity(ConnectActivity::class.java, intent) + .create() + .postCreate(null) + .start() + .resume() + ShadowLooper.idleMainLooper() + return controller + } + + private fun navHostOf(controller: ActivityController): NavHostFragment = + controller.get().supportFragmentManager.findFragmentById(R.id.nav_host_fragment_connect) as NavHostFragment + + companion object { + private val OPPORTUNITY_PATH = Regex("/opportunity/showcase-opportunity-(\\d+)/") + + @JvmStatic + @ParameterizedRobolectricTestRunner.Parameters(name = "{0}") + fun locales() = L10nScreenshots.parameters() + } +} diff --git a/app/unit-tests/src/org/commcare/l10n/OpportunityListScreenshotTest.kt b/app/unit-tests/src/org/commcare/l10n/OpportunityListScreenshotTest.kt index 54eadfadba..ac3afd8550 100644 --- a/app/unit-tests/src/org/commcare/l10n/OpportunityListScreenshotTest.kt +++ b/app/unit-tests/src/org/commcare/l10n/OpportunityListScreenshotTest.kt @@ -22,7 +22,7 @@ class OpportunityListScreenshotTest( ) : BaseConnectScreenshotTest(target) { @Before fun seedOpportunities() { - ConnectJobUtils.storeJobs(ApplicationProvider.getApplicationContext(), ConnectScreenshotData.opportunities(), true) + ConnectJobUtils.storeJobs(ApplicationProvider.getApplicationContext(), ConnectScreenshotData.buildOpportunities(), true) ConnectSyncPreferences.getInstance().apply { markSessionStart() storeLastSyncTime(ConnectRepository.SYNC_KEY_OPPORTUNITIES) diff --git a/app/unit-tests/src/org/commcare/l10n/ProfileScreenshotTests.kt b/app/unit-tests/src/org/commcare/l10n/ProfileScreenshotTests.kt index 49b7f5c449..b62eb54622 100644 --- a/app/unit-tests/src/org/commcare/l10n/ProfileScreenshotTests.kt +++ b/app/unit-tests/src/org/commcare/l10n/ProfileScreenshotTests.kt @@ -21,7 +21,7 @@ class ProfileScreenshotTest( @get:Rule val localeRule: TestRule = L10nScreenshots.localeRule(target) - override fun buildUser(): ConnectUserRecord = ConnectScreenshotData.user() + override fun buildUser(): ConnectUserRecord = ConnectScreenshotData.buildUser() @Test fun profile() = L10nScreenshots.capture(activityController, target, "40_personalid_profile") @@ -42,7 +42,7 @@ class ProfileEditScreenshotTest( @get:Rule val localeRule: TestRule = L10nScreenshots.localeRule(target) - override fun buildUser(): ConnectUserRecord = ConnectScreenshotData.user() + override fun buildUser(): ConnectUserRecord = ConnectScreenshotData.buildUser() @Test fun profileEdit() { diff --git a/app/unit-tests/src/org/commcare/l10n/WorkHistoryScreenshotTest.kt b/app/unit-tests/src/org/commcare/l10n/WorkHistoryScreenshotTest.kt index cf827c1c71..b8139fe208 100644 --- a/app/unit-tests/src/org/commcare/l10n/WorkHistoryScreenshotTest.kt +++ b/app/unit-tests/src/org/commcare/l10n/WorkHistoryScreenshotTest.kt @@ -25,7 +25,7 @@ class WorkHistoryScreenshotTest( @Before fun serveWorkHistory() { personalIdApi.start() - personalIdApi.server.enqueue(MockResponse().setResponseCode(200).setBody(ConnectScreenshotData.workHistoryResponse())) + personalIdApi.server.enqueue(MockResponse().setResponseCode(200).setBody(ConnectScreenshotData.buildWorkHistoryJson())) } @After