diff --git a/Corona-Warn-App/build.gradle b/Corona-Warn-App/build.gradle index 81c5fa348604877b1342e01047824d5587104f79..d946fb4e01f45225e407578895b36a28663bdf30 100644 --- a/Corona-Warn-App/build.gradle +++ b/Corona-Warn-App/build.gradle @@ -64,6 +64,7 @@ android { arguments += ["room.schemaLocation": "$projectDir/schemas".toString()] } } + vectorDrawables.useSupportLibrary = true } def signingPropFile = file("../keystore.properties") diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt new file mode 100644 index 0000000000000000000000000000000000000000..97a76ddf8297d79305f7382d0a3576c5fd0e30e2 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt @@ -0,0 +1,132 @@ +package de.rki.coronawarnapp.ui.main.home + +import de.rki.coronawarnapp.risk.RiskState +import de.rki.coronawarnapp.submission.ui.homecards.FetchingResult +import de.rki.coronawarnapp.submission.ui.homecards.NoTest +import de.rki.coronawarnapp.submission.ui.homecards.SubmissionDone +import de.rki.coronawarnapp.submission.ui.homecards.TestError +import de.rki.coronawarnapp.submission.ui.homecards.TestErrorCard +import de.rki.coronawarnapp.submission.ui.homecards.TestFetchingCard +import de.rki.coronawarnapp.submission.ui.homecards.TestInvalid +import de.rki.coronawarnapp.submission.ui.homecards.TestInvalidCard +import de.rki.coronawarnapp.submission.ui.homecards.TestNegative +import de.rki.coronawarnapp.submission.ui.homecards.TestNegativeCard +import de.rki.coronawarnapp.submission.ui.homecards.TestPending +import de.rki.coronawarnapp.submission.ui.homecards.TestPendingCard +import de.rki.coronawarnapp.submission.ui.homecards.TestPositive +import de.rki.coronawarnapp.submission.ui.homecards.TestPositiveCard +import de.rki.coronawarnapp.submission.ui.homecards.TestSubmissionDoneCard +import de.rki.coronawarnapp.submission.ui.homecards.TestUnregisteredCard +import de.rki.coronawarnapp.tracing.TracingProgress +import de.rki.coronawarnapp.tracing.states.IncreasedRisk +import de.rki.coronawarnapp.tracing.states.LowRisk +import de.rki.coronawarnapp.tracing.states.TracingDisabled +import de.rki.coronawarnapp.tracing.states.TracingFailed +import de.rki.coronawarnapp.tracing.states.TracingInProgress +import de.rki.coronawarnapp.tracing.ui.homecards.IncreasedRiskCard +import de.rki.coronawarnapp.tracing.ui.homecards.LowRiskCard +import de.rki.coronawarnapp.tracing.ui.homecards.TracingDisabledCard +import de.rki.coronawarnapp.tracing.ui.homecards.TracingFailedCard +import de.rki.coronawarnapp.tracing.ui.homecards.TracingProgressCard +import org.joda.time.Instant + +object HomeData { + + object Tracing { + val LOW_RISK_ITEM = LowRiskCard.Item( + state = LowRisk( + riskState = RiskState.LOW_RISK, + isInDetailsMode = false, + lastExposureDetectionTime = Instant.now(), + allowManualUpdate = false, + daysWithEncounters = 1, + activeTracingDays = 1 + ), + onCardClick = {}, + onUpdateClick = {} + ) + + val INCREASED_RISK_ITEM = IncreasedRiskCard.Item( + state = IncreasedRisk( + riskState = RiskState.INCREASED_RISK, + isInDetailsMode = false, + lastExposureDetectionTime = Instant.now(), + allowManualUpdate = false, + daysWithEncounters = 1, + activeTracingDays = 1, + lastEncounterAt = Instant.now() + ), + onCardClick = {}, + onUpdateClick = {} + ) + + val TRACING_DISABLED_ITEM = TracingDisabledCard.Item( + state = TracingDisabled( + riskState = RiskState.LOW_RISK, + isInDetailsMode = false, + lastExposureDetectionTime = Instant.now() + ), + onCardClick = {}, + onEnableTracingClick = {} + ) + + val TRACING_PROGRESS_ITEM = TracingProgressCard.Item( + state = TracingInProgress( + riskState = RiskState.LOW_RISK, + isInDetailsMode = false, + tracingProgress = TracingProgress.Downloading + ), + onCardClick = {} + ) + + val TRACING_FAILED_ITEM = TracingFailedCard.Item( + state = TracingFailed( + riskState = RiskState.CALCULATION_FAILED, + isInDetailsMode = false, + lastExposureDetectionTime = Instant.now() + ), + onCardClick = {}, + onRetryClick = {} + ) + } + + object Submission { + val TEST_UNREGISTERED_ITEM = TestUnregisteredCard.Item( + state = NoTest, + onClickAction = {} + ) + + val TEST_FETCHING_ITEM = TestFetchingCard.Item( + state = FetchingResult + ) + + val TEST_POSITIVE_ITEM = TestPositiveCard.Item( + state = TestPositive, + onClickAction = {} + ) + + val TEST_NEGATIVE_ITEM = TestNegativeCard.Item( + state = TestNegative, + onClickAction = {} + ) + + val TEST_INVALID_ITEM = TestInvalidCard.Item( + state = TestInvalid, + onDeleteTest = {} + ) + + val TEST_ERROR_ITEM = TestErrorCard.Item( + state = TestError, + onDeleteTest = {} + ) + + val TEST_PENDING_ITEM = TestPendingCard.Item( + state = TestPending, + onClickAction = {} + ) + + val TEST_SUBMISSION_DONE_ITEM = TestSubmissionDoneCard.Item( + state = SubmissionDone + ) + } +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt index 40cc3e0866dd6858cf03c8426181102cc0ac06a3..16407e5e50e445b5842185f8f702af4e0910b221 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt @@ -1,40 +1,95 @@ package de.rki.coronawarnapp.ui.main.home import androidx.fragment.app.testing.launchFragment -import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.appconfig.AppConfigProvider +import de.rki.coronawarnapp.main.CWASettings +import de.rki.coronawarnapp.notification.TestResultNotificationService +import de.rki.coronawarnapp.storage.TracingRepository +import de.rki.coronawarnapp.submission.SubmissionRepository +import de.rki.coronawarnapp.submission.ui.homecards.SubmissionStateProvider +import de.rki.coronawarnapp.submission.ui.homecards.TestPositiveCard +import de.rki.coronawarnapp.submission.ui.homecards.TestResultItem +import de.rki.coronawarnapp.submission.ui.homecards.TestSubmissionDoneCard +import de.rki.coronawarnapp.tracing.GeneralTracingStatus +import de.rki.coronawarnapp.tracing.states.TracingStateProvider +import de.rki.coronawarnapp.tracing.ui.homecards.TracingStateItem +import de.rki.coronawarnapp.tracing.ui.statusbar.TracingHeaderState +import de.rki.coronawarnapp.ui.main.home.items.DiaryCard +import de.rki.coronawarnapp.ui.main.home.items.FAQCard +import de.rki.coronawarnapp.ui.main.home.items.HomeItem +import de.rki.coronawarnapp.util.security.EncryptionErrorResetTool +import de.rki.coronawarnapp.util.ui.SingleLiveEvent import io.mockk.MockKAnnotations import io.mockk.Runs import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just +import io.mockk.spyk import io.mockk.verify import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import testhelpers.BaseUITest +import testhelpers.SCREENSHOT_DELAY_TIME +import testhelpers.Screenshot +import testhelpers.SystemUIDemoModeRule +import testhelpers.TestDispatcherProvider +import testhelpers.launchFragmentInContainer2 +import testhelpers.recyclerScrollTo +import timber.log.Timber +import tools.fastlane.screengrab.Screengrab +import tools.fastlane.screengrab.locale.LocaleTestRule @RunWith(AndroidJUnit4::class) class HomeFragmentTest : BaseUITest() { - @MockK lateinit var viewModel: HomeFragmentViewModel + @MockK lateinit var errorResetTool: EncryptionErrorResetTool + @MockK lateinit var tracingStatus: GeneralTracingStatus + @MockK lateinit var tracingStateProviderFactory: TracingStateProvider.Factory + @MockK lateinit var submissionStateProvider: SubmissionStateProvider + @MockK lateinit var tracingRepository: TracingRepository + @MockK lateinit var testResultNotificationService: TestResultNotificationService + @MockK lateinit var submissionRepository: SubmissionRepository + @MockK lateinit var cwaSettings: CWASettings + @MockK lateinit var appConfigProvider: AppConfigProvider + + private lateinit var viewModel: HomeFragmentViewModel + + @Rule + @JvmField + val localeTestRule = LocaleTestRule() + + @get:Rule + val systemUIDemoModeRule = SystemUIDemoModeRule() @Before fun setup() { MockKAnnotations.init(this, relaxed = true) + viewModel = homeFragmentViewModelSpy() + with(viewModel) { + every { observeTestResultToSchedulePositiveTestResultReminder() } just Runs + every { refreshRequiredData() } just Runs + every { tracingHeaderState } returns MutableLiveData(TracingHeaderState.TracingActive) + every { showLoweredRiskLevelDialog } returns MutableLiveData() + every { homeItems } returns MutableLiveData(emptyList()) + every { popupEvents } returns SingleLiveEvent() + } - every { viewModel.tracingHeaderState } returns MutableLiveData() - every { viewModel.homeItems } returns MutableLiveData(emptyList()) - every { viewModel.refreshRequiredData() } just Runs - - setupMockViewModel(object : HomeFragmentViewModel.Factory { - // override fun create(handle: SavedStateHandle){} HomeFragmentViewModel = viewModel} - override fun create(): HomeFragmentViewModel = viewModel - }) + setupMockViewModel( + object : HomeFragmentViewModel.Factory { + override fun create(): HomeFragmentViewModel = viewModel + } + ) } @After @@ -44,11 +99,165 @@ class HomeFragmentTest : BaseUITest() { @Test fun onResumeCallsRefresh() { - launchFragment<HomeFragment>().apply { - moveToState(Lifecycle.State.RESUMED) - verify(exactly = 1) { viewModel.refreshRequiredData() } - } + // AppTheme is required here to prevent xml inflation crash + launchFragment<HomeFragment>(themeResId = R.style.AppTheme) + verify(exactly = 1) { viewModel.refreshRequiredData() } + } + + @Screenshot + @Test + fun capture_screenshot_low_risk() { + every { viewModel.homeItems } returns itemsLiveData( + HomeData.Tracing.LOW_RISK_ITEM + ) + + captureScreenshot("low_risk") + onView(withId(R.id.recycler_view)).perform(recyclerScrollTo()) + Thread.sleep(SCREENSHOT_DELAY_TIME) + Screengrab.screenshot(HomeFragment::class.simpleName.plus("low_risk_2")) + } + + @Screenshot + @Test + fun capture_screenshot_increased_risk() { + every { viewModel.homeItems } returns itemsLiveData( + HomeData.Tracing.INCREASED_RISK_ITEM + ) + captureScreenshot("increased_risk") + } + + @Screenshot + @Test + fun capture_screenshot_tracing_disabled() { + every { viewModel.tracingHeaderState } returns MutableLiveData(TracingHeaderState.TracingInActive) + every { viewModel.homeItems } returns itemsLiveData( + HomeData.Tracing.TRACING_DISABLED_ITEM + ) + captureScreenshot("tracing_disabled") + } + + @Screenshot + @Test + fun capture_screenshot_tracing_progress_downloading() { + every { viewModel.homeItems } returns itemsLiveData( + HomeData.Tracing.TRACING_PROGRESS_ITEM + ) + captureScreenshot("progress_downloading") + } + + @Screenshot + @Test + fun capture_screenshot_tracing_failed() { + every { viewModel.homeItems } returns itemsLiveData( + HomeData.Tracing.TRACING_FAILED_ITEM + ) + captureScreenshot("tracing_failed") + } + + @Screenshot + @Test + fun capture_screenshot_test_submission_done() { + every { viewModel.homeItems } returns itemsLiveData( + submissionTestResultItem = HomeData.Submission.TEST_SUBMISSION_DONE_ITEM + ) + captureScreenshot("submission_done") + } + + @Screenshot + @Test + fun capture_screenshot_test_error() { + every { viewModel.homeItems } returns itemsLiveData( + submissionTestResultItem = HomeData.Submission.TEST_ERROR_ITEM + ) + captureScreenshot("test_error") + } + + @Screenshot + @Test + fun capture_screenshot_test_fetching() { + every { viewModel.homeItems } returns itemsLiveData( + submissionTestResultItem = HomeData.Submission.TEST_FETCHING_ITEM + ) + captureScreenshot("test_fetching") + } + + @Screenshot + @Test + fun capture_screenshot_test_invalid() { + every { viewModel.homeItems } returns itemsLiveData( + submissionTestResultItem = HomeData.Submission.TEST_INVALID_ITEM + ) + captureScreenshot("test_invalid") + } + + @Screenshot + @Test + fun capture_screenshot_test_negative() { + every { viewModel.homeItems } returns itemsLiveData( + submissionTestResultItem = HomeData.Submission.TEST_NEGATIVE_ITEM + ) + captureScreenshot("test_negative") + } + + @Screenshot + @Test + fun capture_screenshot_test_positive() { + every { viewModel.homeItems } returns itemsLiveData( + submissionTestResultItem = HomeData.Submission.TEST_POSITIVE_ITEM + ) + captureScreenshot("test_positive") + } + + @Screenshot + @Test + fun capture_screenshot_test_pending() { + every { viewModel.homeItems } returns itemsLiveData( + submissionTestResultItem = HomeData.Submission.TEST_PENDING_ITEM + ) + captureScreenshot("test_pending") + } + + private fun captureScreenshot(nameSuffix: String) { + val name = HomeFragment::class.simpleName + "_" + nameSuffix + launchFragmentInContainer2<HomeFragment>() + Thread.sleep(SCREENSHOT_DELAY_TIME) + Screengrab.screenshot(name) } + + private fun itemsLiveData( + tracingStateItem: TracingStateItem = HomeData.Tracing.LOW_RISK_ITEM, + submissionTestResultItem: TestResultItem = HomeData.Submission.TEST_UNREGISTERED_ITEM + ): LiveData<List<HomeItem>> = + MutableLiveData( + mutableListOf<HomeItem>().apply { + when (submissionTestResultItem) { + is TestSubmissionDoneCard.Item, + is TestPositiveCard.Item -> { + Timber.d("Tracing item is not added, submission:$submissionTestResultItem") + } + else -> add(tracingStateItem) + } + + add(submissionTestResultItem) + add(DiaryCard.Item {}) + add(FAQCard.Item {}) + } + ) + + private fun homeFragmentViewModelSpy() = spyk( + HomeFragmentViewModel( + dispatcherProvider = TestDispatcherProvider, + errorResetTool = errorResetTool, + tracingRepository = tracingRepository, + tracingStateProviderFactory = tracingStateProviderFactory, + testResultNotificationService = testResultNotificationService, + appConfigProvider = appConfigProvider, + tracingStatus = tracingStatus, + submissionRepository = submissionRepository, + submissionStateProvider = submissionStateProvider, + cwaSettings = cwaSettings + ) + ) } @Module diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentTest.kt index 7ea43ae382148ef75a7c98fde5dd45e9311c8e94..829ed1551d57246345e1a7f7457f7be0762e36bf 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentTest.kt @@ -1,14 +1,9 @@ package de.rki.coronawarnapp.ui.onboarding import androidx.fragment.app.testing.launchFragment -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector -import de.rki.coronawarnapp.R import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository import io.mockk.MockKAnnotations import io.mockk.impl.annotations.MockK @@ -24,6 +19,7 @@ import testhelpers.TestDispatcherProvider import testhelpers.launchFragmentInContainer2 import tools.fastlane.screengrab.Screengrab import tools.fastlane.screengrab.locale.LocaleTestRule +import testhelpers.SCREENSHOT_DELAY_TIME @RunWith(AndroidJUnit4::class) class OnboardingDeltaInteroperabilityFragmentTest : BaseUITest() { @@ -64,8 +60,7 @@ class OnboardingDeltaInteroperabilityFragmentTest : BaseUITest() { @Test fun capture_screenshot() { launchFragmentInContainer2<OnboardingDeltaInteroperabilityFragment>() - // Check any view to make sure screenshot is not blank - onView(withId(R.id.onboarding_button_next)).check(matches(isDisplayed())) + Thread.sleep(SCREENSHOT_DELAY_TIME) Screengrab.screenshot(OnboardingDeltaInteroperabilityFragment::class.simpleName) } } diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingFragmentTest.kt index 15810f4313b49a52dfb46eb3b07ed76055baa18c..4244122bcf89a71a3b9a183d0b19b9de1e182cc5 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingFragmentTest.kt @@ -4,8 +4,6 @@ import androidx.fragment.app.testing.launchFragment import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.scrollTo -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module @@ -17,6 +15,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import testhelpers.BaseUITest +import testhelpers.SCREENSHOT_DELAY_TIME import testhelpers.Screenshot import testhelpers.SystemUIDemoModeRule import testhelpers.launchFragmentInContainer2 @@ -54,8 +53,7 @@ class OnboardingFragmentTest : BaseUITest() { @Test fun capture_screenshot() { launchFragmentInContainer2<OnboardingFragment>() - // Check any view to make sure screenshot is not blank - onView(withId(R.id.onboarding_button_next)).check(matches(isDisplayed())) + Thread.sleep(SCREENSHOT_DELAY_TIME) Screengrab.screenshot(OnboardingFragment::class.simpleName) onView(withId(R.id.onboarding_easy_language)).perform(scrollTo(), click()) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingNotificationsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingNotificationsFragmentTest.kt index 36b9ab7b6e98ee868d5cd88be9a49ba19a5a9405..dc701f6603ff952ff84943e242b2ecc4f255b1f6 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingNotificationsFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingNotificationsFragmentTest.kt @@ -1,20 +1,16 @@ package de.rki.coronawarnapp.ui.onboarding import androidx.fragment.app.testing.launchFragment -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector -import de.rki.coronawarnapp.R import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import testhelpers.BaseUITest +import testhelpers.SCREENSHOT_DELAY_TIME import testhelpers.Screenshot import testhelpers.SystemUIDemoModeRule import testhelpers.launchFragmentInContainer2 @@ -52,8 +48,7 @@ class OnboardingNotificationsFragmentTest : BaseUITest() { @Test fun capture_screenshot() { launchFragmentInContainer2<OnboardingNotificationsFragment>() - // Check any view to make sure screenshot is not blank - onView(withId(R.id.onboarding_button_next)).check(matches(isDisplayed())) + Thread.sleep(SCREENSHOT_DELAY_TIME) Screengrab.screenshot(OnboardingNotificationsFragment::class.simpleName) } } diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyFragmentTest.kt index 31f7e424436ef647f6cd6cf974f3f8401213f5d5..05354b4bb60745654470eb9e5201b1d3f40fa580 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyFragmentTest.kt @@ -1,19 +1,16 @@ package de.rki.coronawarnapp.ui.onboarding import androidx.fragment.app.testing.launchFragment -import androidx.test.espresso.Espresso -import androidx.test.espresso.assertion.ViewAssertions -import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector -import de.rki.coronawarnapp.R import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import testhelpers.BaseUITest +import testhelpers.SCREENSHOT_DELAY_TIME import testhelpers.Screenshot import testhelpers.SystemUIDemoModeRule import testhelpers.launchFragmentInContainer2 @@ -51,9 +48,7 @@ class OnboardingPrivacyFragmentTest : BaseUITest() { @Test fun capture_screenshot() { launchFragmentInContainer2<OnboardingPrivacyFragment>() - // Check any view to make sure screenshot is not blank - Espresso.onView(ViewMatchers.withId(R.id.onboarding_button_next)) - .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Thread.sleep(SCREENSHOT_DELAY_TIME) Screengrab.screenshot(OnboardingPrivacyFragment::class.simpleName) } } diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTestFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTestFragmentTest.kt index 3c0a2631fd760247bbad8faa5c668342854fea77..f8d350f665cd81ffb4de089e9b488f2052af65b1 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTestFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTestFragmentTest.kt @@ -1,14 +1,9 @@ package de.rki.coronawarnapp.ui.onboarding import androidx.fragment.app.testing.launchFragment -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector -import de.rki.coronawarnapp.R import org.junit.After import org.junit.Before import org.junit.Rule @@ -20,6 +15,7 @@ import testhelpers.SystemUIDemoModeRule import testhelpers.launchFragmentInContainer2 import tools.fastlane.screengrab.Screengrab import tools.fastlane.screengrab.locale.LocaleTestRule +import testhelpers.SCREENSHOT_DELAY_TIME @RunWith(AndroidJUnit4::class) class OnboardingTestFragmentTest : BaseUITest() { @@ -52,8 +48,7 @@ class OnboardingTestFragmentTest : BaseUITest() { @Test fun capture_screenshot() { launchFragmentInContainer2<OnboardingTestFragment>() - // Check any view to make sure screenshot is not blank - onView(withId(R.id.onboarding_button_next)).check(matches(isDisplayed())) + Thread.sleep(SCREENSHOT_DELAY_TIME) Screengrab.screenshot(OnboardingTestFragment::class.simpleName) } } diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentTest.kt index fe0c93bf5a7eea060e658b66c1f67af0d6d45154..a6be434831ba9b3312b5472a91ea776984e8cff9 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentTest.kt @@ -1,14 +1,9 @@ package de.rki.coronawarnapp.ui.onboarding import androidx.fragment.app.testing.launchFragment -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector -import de.rki.coronawarnapp.R import de.rki.coronawarnapp.nearby.TracingPermissionHelper import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository import io.mockk.MockKAnnotations @@ -17,12 +12,14 @@ import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just import io.mockk.spyk +import kotlinx.coroutines.flow.flowOf import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import testhelpers.BaseUITest +import testhelpers.SCREENSHOT_DELAY_TIME import testhelpers.Screenshot import testhelpers.SystemUIDemoModeRule import testhelpers.TestDispatcherProvider @@ -56,6 +53,7 @@ class OnboardingTracingFragmentTest : BaseUITest() { ) every { viewModelSpy.resetTracing() } just Runs + every { interopRepo.countryList } returns flowOf() setupMockViewModel(object : OnboardingTracingFragmentViewModel.Factory { override fun create(): OnboardingTracingFragmentViewModel = viewModelSpy @@ -77,8 +75,7 @@ class OnboardingTracingFragmentTest : BaseUITest() { fun capture_screenshot() { val simpleName = OnboardingTracingFragment::class.simpleName launchFragmentInContainer2<OnboardingTracingFragment>() - // Check any view to make sure screenshot is not blank - onView(withId(R.id.onboarding_button_next)).check(matches(isDisplayed())) + Thread.sleep(SCREENSHOT_DELAY_TIME) Screengrab.screenshot(simpleName) } } diff --git a/Corona-Warn-App/src/androidTest/java/testhelpers/RecyclerViewScrollAction.kt b/Corona-Warn-App/src/androidTest/java/testhelpers/RecyclerViewScrollAction.kt new file mode 100644 index 0000000000000000000000000000000000000000..f28f3560013c6fcb960333ce14b5164e4cf8cca7 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/testhelpers/RecyclerViewScrollAction.kt @@ -0,0 +1,30 @@ +package testhelpers + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import org.hamcrest.CoreMatchers.allOf +import org.hamcrest.Matcher + +fun recyclerScrollTo(position: Int? = null): ViewAction = RecyclerViewScrollAction(position) + +private class RecyclerViewScrollAction(private val position: Int? = null) : ViewAction { + override fun getDescription(): String { + return "scroll RecyclerView to bottom" + } + + override fun getConstraints(): Matcher<View> { + return allOf(isAssignableFrom(RecyclerView::class.java), isDisplayed()) + } + + override fun perform(uiController: UiController?, view: View?) { + val recyclerView = view as RecyclerView + val itemCount = recyclerView.adapter?.itemCount + val itemPosition = position ?: itemCount?.minus(1) ?: 0 + recyclerView.scrollToPosition(itemPosition) + uiController?.loopMainThreadUntilIdle() + } +} diff --git a/Corona-Warn-App/src/androidTest/java/testhelpers/TestApplication.kt b/Corona-Warn-App/src/androidTest/java/testhelpers/TestApplication.kt index dc215f0144b1ab17d0d34e59634b69a206e08784..090469fdfc056c1c215e5b79d432fd6b1d8c787a 100644 --- a/Corona-Warn-App/src/androidTest/java/testhelpers/TestApplication.kt +++ b/Corona-Warn-App/src/androidTest/java/testhelpers/TestApplication.kt @@ -30,12 +30,14 @@ class TestApplication : Application(), HasAndroidInjector { private fun setupActivityHook() { registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { - override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) { + override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) = Unit + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + Timber.d("onActivityCreated") setupFragmentHook(activity) + Timber.d("FragmentHook injection is called") } - override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = Unit - override fun onActivityStarted(activity: Activity) = Unit override fun onActivityResumed(activity: Activity) = Unit @@ -44,7 +46,7 @@ class TestApplication : Application(), HasAndroidInjector { override fun onActivityStopped(activity: Activity) = Unit - override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle?) = Unit + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit override fun onActivityDestroyed(activity: Activity) = Unit }) diff --git a/Corona-Warn-App/src/androidTest/java/testhelpers/TestExtensions.kt b/Corona-Warn-App/src/androidTest/java/testhelpers/TestExtensions.kt index 601202d139449d7ad1265b5e91d36457724a9e2e..9dc088f42d7578a595ca7d0dcb95a33d2d9ef6bf 100644 --- a/Corona-Warn-App/src/androidTest/java/testhelpers/TestExtensions.kt +++ b/Corona-Warn-App/src/androidTest/java/testhelpers/TestExtensions.kt @@ -7,6 +7,10 @@ import androidx.fragment.app.FragmentFactory import androidx.fragment.app.testing.FragmentScenario import de.rki.coronawarnapp.R +/** Delay time before taking screenshot + */ +const val SCREENSHOT_DELAY_TIME = 2000L + /** * Launches Fragment in Activity root container. * Same as [androidx.fragment.app.testing.launchFragmentInContainer] except that it defaults diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataBindingAdapters.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataBindingAdapters.kt index cba12344fa40dceb23b2b495ca48984dbcc8cdab..5147b479bd9c2cbd70fdd398953d726b5687afed 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataBindingAdapters.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataBindingAdapters.kt @@ -11,7 +11,6 @@ import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieDrawable import com.airbnb.lottie.LottieProperty import com.airbnb.lottie.model.KeyPath -import de.rki.coronawarnapp.CoronaWarnApplication import de.rki.coronawarnapp.util.ContextExtensions.getDrawableCompat const val IGNORE_CHANGE_TAG = "ignore" @@ -29,11 +28,11 @@ fun setChecked(switch: Switch, status: Boolean?) { @BindingAdapter("animation") fun setAnimation(view: LottieAnimationView, animation: Int?) { if (animation != null) { - val appContext = CoronaWarnApplication.getAppContext() - val type = appContext.resources.getResourceTypeName(animation) + val context = view.context + val type = context.resources.getResourceTypeName(animation) if (type == DRAWABLE_TYPE) { - view.setImageDrawable(appContext.getDrawableCompat(animation)) + view.setImageDrawable(context.getDrawableCompat(animation)) } else { view.setAnimation(animation) view.repeatCount = LottieDrawable.INFINITE diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_homescreen_card_include.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_homescreen_card_include.xml index 724db701a43c20e48936245b01802ca905be3efe..390d2b13cf7b3690a5cbb5178106cc0717f36be4 100644 --- a/Corona-Warn-App/src/main/res/layout/contact_diary_homescreen_card_include.xml +++ b/Corona-Warn-App/src/main/res/layout/contact_diary_homescreen_card_include.xml @@ -34,7 +34,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/contact_diary_card_homescreen_title" /> - <ImageView + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/contact_diary_card_homescreen_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/Corona-Warn-App/src/main/res/layout/home_submission_status_card_error.xml b/Corona-Warn-App/src/main/res/layout/home_submission_status_card_error.xml index 354525d2570ffa9e54441071966b071a12ec51d7..63aa89dc006172f5cdb9f4abd4945d877388f53c 100644 --- a/Corona-Warn-App/src/main/res/layout/home_submission_status_card_error.xml +++ b/Corona-Warn-App/src/main/res/layout/home_submission_status_card_error.xml @@ -52,7 +52,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/subtitle" /> - <ImageView + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/Corona-Warn-App/src/main/res/layout/home_submission_status_card_invalid.xml b/Corona-Warn-App/src/main/res/layout/home_submission_status_card_invalid.xml index 725283a5de36bd9bf5a9ca9b622dda7ecc182722..2c16e8dfc436d69b8fee51894c38694a415da11b 100644 --- a/Corona-Warn-App/src/main/res/layout/home_submission_status_card_invalid.xml +++ b/Corona-Warn-App/src/main/res/layout/home_submission_status_card_invalid.xml @@ -36,7 +36,7 @@ app:layout_constraintStart_toStartOf="@+id/title" app:layout_constraintTop_toBottomOf="@+id/title" /> - <ImageView + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/Corona-Warn-App/src/main/res/layout/home_submission_status_card_negative.xml b/Corona-Warn-App/src/main/res/layout/home_submission_status_card_negative.xml index 4b8c0df4532909f0345b052769a7b52687af60d1..74b0a58c7f43aeb1b90d54c380820aa6f6f8ab65 100644 --- a/Corona-Warn-App/src/main/res/layout/home_submission_status_card_negative.xml +++ b/Corona-Warn-App/src/main/res/layout/home_submission_status_card_negative.xml @@ -52,7 +52,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/subtitle" /> - <ImageView + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/Corona-Warn-App/src/main/res/layout/home_submission_status_card_pending.xml b/Corona-Warn-App/src/main/res/layout/home_submission_status_card_pending.xml index 4270b6c2f2dd7398c58607ab90a206f7f3eb2cfc..9a2257a3cc6c8559d8fb024905bb8b786a8ab7b0 100644 --- a/Corona-Warn-App/src/main/res/layout/home_submission_status_card_pending.xml +++ b/Corona-Warn-App/src/main/res/layout/home_submission_status_card_pending.xml @@ -37,7 +37,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/title" /> - <ImageView + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/Corona-Warn-App/src/main/res/layout/home_submission_status_card_unregistered.xml b/Corona-Warn-App/src/main/res/layout/home_submission_status_card_unregistered.xml index 4827ffe20bc346586f167b4e4a7fa2c8dcabc260..e7f47cc6b0a30c5f92dda088514df2bcf89fc3d0 100644 --- a/Corona-Warn-App/src/main/res/layout/home_submission_status_card_unregistered.xml +++ b/Corona-Warn-App/src/main/res/layout/home_submission_status_card_unregistered.xml @@ -37,7 +37,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/title" /> - <ImageView + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/Corona-Warn-App/src/main/res/layout/include_submission_behaviour_row.xml b/Corona-Warn-App/src/main/res/layout/include_submission_behaviour_row.xml index fe0e972da7646460094ae25b08f9c9de09f94f95..4f35ba76999d27d22ee9fddfd3ce8c9923c0f5dd 100644 --- a/Corona-Warn-App/src/main/res/layout/include_submission_behaviour_row.xml +++ b/Corona-Warn-App/src/main/res/layout/include_submission_behaviour_row.xml @@ -26,7 +26,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> - <ImageView + <androidx.appcompat.widget.AppCompatImageView style="@style/icon" android:layout_width="@dimen/icon_size_risk_details_behavior" android:layout_height="@dimen/icon_size_risk_details_behavior" diff --git a/Corona-Warn-App/src/main/res/layout/tracing_content_disabled_view.xml b/Corona-Warn-App/src/main/res/layout/tracing_content_disabled_view.xml index 994e7984e880c49daa434ed96ed270726a9948f6..827d81c0156ad03c52f36737ba861c7949a6938c 100644 --- a/Corona-Warn-App/src/main/res/layout/tracing_content_disabled_view.xml +++ b/Corona-Warn-App/src/main/res/layout/tracing_content_disabled_view.xml @@ -28,7 +28,7 @@ app:layout_constraintTop_toTopOf="parent" app:layout_goneMarginEnd="0dp" /> - <ImageView + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/details_icon" gone="@{state.isInDetailsMode}" android:layout_width="@dimen/icon_size_risk_card" diff --git a/Corona-Warn-App/src/main/res/layout/tracing_content_failed_view.xml b/Corona-Warn-App/src/main/res/layout/tracing_content_failed_view.xml index 8cd918e4d5afa7f952fdf6b9251bbe8ccb7bee1d..101480a1308bbae54cce8732059f937b7f994380 100644 --- a/Corona-Warn-App/src/main/res/layout/tracing_content_failed_view.xml +++ b/Corona-Warn-App/src/main/res/layout/tracing_content_failed_view.xml @@ -27,7 +27,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - <ImageView + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/risk_card_header_icon" gone="@{state.isInDetailsMode}" android:layout_width="@dimen/icon_size_risk_card" diff --git a/Corona-Warn-App/src/main/res/layout/tracing_content_increased_view.xml b/Corona-Warn-App/src/main/res/layout/tracing_content_increased_view.xml index 3041ca8fb3c8d9055420ab016a38d877407212be..bc98c30484e1ce8dfa4ca779ce006bd9c557c2e3 100644 --- a/Corona-Warn-App/src/main/res/layout/tracing_content_increased_view.xml +++ b/Corona-Warn-App/src/main/res/layout/tracing_content_increased_view.xml @@ -29,7 +29,7 @@ app:layout_constraintTop_toTopOf="parent" app:layout_goneMarginEnd="0dp" /> - <ImageView + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/details_icon" gone="@{state.isInDetailsMode}" android:layout_width="@dimen/icon_size_risk_card" diff --git a/Corona-Warn-App/src/main/res/layout/tracing_content_low_view.xml b/Corona-Warn-App/src/main/res/layout/tracing_content_low_view.xml index 8ef03ec4033370b19d1ce907975ed56d68da5092..a73f7d0c409a4c83d3c84132ec949a0db3763b62 100644 --- a/Corona-Warn-App/src/main/res/layout/tracing_content_low_view.xml +++ b/Corona-Warn-App/src/main/res/layout/tracing_content_low_view.xml @@ -31,16 +31,16 @@ tools:text="@string/risk_card_low_risk_headline" tools:textColor="@color/colorStableLight" /> - <ImageView + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/details_icon" gone="@{state.isInDetailsMode}" android:layout_width="@dimen/icon_size_risk_card" android:layout_height="@dimen/icon_size_risk_card" android:importantForAccessibility="no" android:src="@drawable/ic_forward" - app:tint="@color/colorStableLight" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" + app:tint="@color/colorStableLight" tools:tint="@color/colorStableLight" /> <de.rki.coronawarnapp.ui.view.TracingCardInfoRow diff --git a/Corona-Warn-App/src/main/res/layout/tracing_content_progress_view.xml b/Corona-Warn-App/src/main/res/layout/tracing_content_progress_view.xml index 38c3404db2513f7c1ea1d4efcce80b3c3cb5cf0e..b425d258b53994166fe68cea2109ff11a51aedcf 100644 --- a/Corona-Warn-App/src/main/res/layout/tracing_content_progress_view.xml +++ b/Corona-Warn-App/src/main/res/layout/tracing_content_progress_view.xml @@ -29,7 +29,7 @@ app:layout_goneMarginEnd="0dp" tools:text="Daten werden herunter geladen" /> - <ImageView + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/details_icon" gone="@{state.isInDetailsMode}" android:layout_width="@dimen/icon_size_risk_card" diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataBindingAdaptersTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataBindingAdaptersTest.kt index 29fbe18962425335417e71c1e23cef77787d9611..ef75a50d41ecb235fac8d6ebb2288293096b0a1a 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataBindingAdaptersTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataBindingAdaptersTest.kt @@ -1,11 +1,11 @@ package de.rki.coronawarnapp.util import android.content.Context +import android.content.res.Resources import android.graphics.drawable.Drawable import android.widget.Switch import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieDrawable -import de.rki.coronawarnapp.CoronaWarnApplication import de.rki.coronawarnapp.R import de.rki.coronawarnapp.util.ContextExtensions.getDrawableCompat import io.mockk.MockKAnnotations @@ -30,7 +30,6 @@ class DataBindingAdaptersTest { @Before fun setUp() { MockKAnnotations.init(this) - mockkObject(CoronaWarnApplication) mockkObject(ContextExtensions) } @@ -70,9 +69,13 @@ class DataBindingAdaptersTest { } private fun setAnimation(animation: Int?) { - every { CoronaWarnApplication.getAppContext().resources.getResourceTypeName(any()) } returns "raw" - - val animationView = mockk<LottieAnimationView>(relaxUnitFun = true) + val animationView = mockk<LottieAnimationView>(relaxUnitFun = true).apply { + every { context } returns mockk<Context>().apply { + every { resources } returns mockk<Resources>().apply { + every { getResourceTypeName(any()) } returns "raw" + } + } + } setAnimation(animationView, animation) @@ -82,6 +85,7 @@ class DataBindingAdaptersTest { if (animation != null) { verifySequence { + animationView.context animationView.setAnimation(animation) animationView.repeatCount = LottieDrawable.INFINITE animationView.repeatMode = LottieDrawable.RESTART @@ -108,10 +112,14 @@ class DataBindingAdaptersTest { } private fun setDrawable(drawableId: Int?) { - every { CoronaWarnApplication.getAppContext().resources.getResourceTypeName(any()) } returns DRAWABLE_TYPE - every { CoronaWarnApplication.getAppContext().getDrawableCompat(any()) } returns drawable - - val animationView = mockk<LottieAnimationView>(relaxUnitFun = true) + val animationView = mockk<LottieAnimationView>(relaxUnitFun = true).apply { + every { context } returns mockk<Context>().apply { + every { resources } returns mockk<Resources>().apply { + every { getResourceTypeName(any()) } returns DRAWABLE_TYPE + } + every { getDrawableCompat(any()) } returns this@DataBindingAdaptersTest.drawable + } + } setAnimation(animationView, drawableId) @@ -124,6 +132,7 @@ class DataBindingAdaptersTest { if (drawableId != null) { verifySequence { + animationView.context animationView.setImageDrawable(any()) } } else { diff --git a/screenshot.sh b/screenshot.sh new file mode 100644 index 0000000000000000000000000000000000000000..2a9c13d5767acd89e3f45427d674be714d72b631 --- /dev/null +++ b/screenshot.sh @@ -0,0 +1,3 @@ +#!/bin/zsh +bundle exec fastlane screengrab +fastlane run zip output_path:"fastlane/metadata/screenshots.zip" path:"fastlane/metadata/android"