diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingFragmentScreenshot.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingFragmentScreenshot.kt new file mode 100644 index 0000000000000000000000000000000000000000..930cfb28b5f3884037e944c2f0ed7b3dcb44347a --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingFragmentScreenshot.kt @@ -0,0 +1,47 @@ +package de.rki.coronawarnapp.ui.onboarding.screenshot + +import android.Manifest +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.GrantPermissionRule +import de.rki.coronawarnapp.ui.onboarding.OnboardingFragment +import de.rki.coronawarnapp.ui.onboarding.OnboardingFragmentViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +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.captureScreenshot + +@RunWith(AndroidJUnit4::class) +class OnboardingFragmentScreenshot : BaseUITest() { + + @get:Rule + val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + + @MockK lateinit var viewModel: OnboardingFragmentViewModel + + @Before + fun setUp() { + MockKAnnotations.init(this, relaxed = true) + + setupMockViewModel(object : OnboardingFragmentViewModel.Factory { + override fun create(): OnboardingFragmentViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun capture_screenshot() { + captureScreenshot<OnboardingFragment>() + } +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingNotificationsFragmentScreenshot.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingNotificationsFragmentScreenshot.kt new file mode 100644 index 0000000000000000000000000000000000000000..6b10761702fdc3c46779f30d6ea311ba25131758 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingNotificationsFragmentScreenshot.kt @@ -0,0 +1,46 @@ +package de.rki.coronawarnapp.ui.onboarding.screenshot + +import android.Manifest +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.GrantPermissionRule +import de.rki.coronawarnapp.ui.onboarding.OnboardingNotificationsFragment +import de.rki.coronawarnapp.ui.onboarding.OnboardingNotificationsViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +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.captureScreenshot + +@RunWith(AndroidJUnit4::class) +class OnboardingNotificationsFragmentScreenshot : BaseUITest() { + + @MockK lateinit var viewModel: OnboardingNotificationsViewModel + + @get:Rule val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + + setupMockViewModel(object : OnboardingNotificationsViewModel.Factory { + override fun create(): OnboardingNotificationsViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun capture_screenshot() { + captureScreenshot<OnboardingNotificationsFragment>() + } +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingPrivacyFragmentScreenshot.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingPrivacyFragmentScreenshot.kt new file mode 100644 index 0000000000000000000000000000000000000000..76344f50011251e838fa69fa7a55e2bb1afbcceb --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingPrivacyFragmentScreenshot.kt @@ -0,0 +1,46 @@ +package de.rki.coronawarnapp.ui.onboarding.screenshot + +import android.Manifest +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.GrantPermissionRule +import de.rki.coronawarnapp.ui.onboarding.OnboardingPrivacyFragment +import de.rki.coronawarnapp.ui.onboarding.OnboardingPrivacyViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +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.captureScreenshot + +@RunWith(AndroidJUnit4::class) +class OnboardingPrivacyFragmentScreenshot : BaseUITest() { + + @MockK lateinit var viewModel: OnboardingPrivacyViewModel + + @get:Rule val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + + setupMockViewModel(object : OnboardingPrivacyViewModel.Factory { + override fun create(): OnboardingPrivacyViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun capture_screenshot() { + captureScreenshot<OnboardingPrivacyFragment>() + } +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingTestFragmentScreenshot.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingTestFragmentScreenshot.kt new file mode 100644 index 0000000000000000000000000000000000000000..30f7c497a4d8083d2a7d0c95cabae16ee1c77be5 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingTestFragmentScreenshot.kt @@ -0,0 +1,46 @@ +package de.rki.coronawarnapp.ui.onboarding.screenshot + +import android.Manifest +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.GrantPermissionRule +import de.rki.coronawarnapp.ui.onboarding.OnboardingTestFragment +import de.rki.coronawarnapp.ui.onboarding.OnboardingTestViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +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.captureScreenshot + +@RunWith(AndroidJUnit4::class) +class OnboardingTestFragmentScreenshot : BaseUITest() { + + @MockK lateinit var viewModel: OnboardingTestViewModel + + @get:Rule val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + + setupMockViewModel(object : OnboardingTestViewModel.Factory { + override fun create(): OnboardingTestViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun capture_screenshot() { + captureScreenshot<OnboardingTestFragment>() + } +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingTracingFragmentScreenshot.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingTracingFragmentScreenshot.kt new file mode 100644 index 0000000000000000000000000000000000000000..768997eb1c504be91301a3f96076ca897399018e --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingTracingFragmentScreenshot.kt @@ -0,0 +1,46 @@ +package de.rki.coronawarnapp.ui.onboarding.screenshot + +import android.Manifest +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.GrantPermissionRule +import de.rki.coronawarnapp.ui.onboarding.OnboardingTracingFragment +import de.rki.coronawarnapp.ui.onboarding.OnboardingTracingFragmentViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +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.captureScreenshot + +@RunWith(AndroidJUnit4::class) +class OnboardingTracingFragmentScreenshot : BaseUITest() { + + @MockK lateinit var viewModel: OnboardingTracingFragmentViewModel + + @get:Rule val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + + setupMockViewModel(object : OnboardingTracingFragmentViewModel.Factory { + override fun create(): OnboardingTracingFragmentViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun capture_screenshot() { + captureScreenshot<OnboardingTracingFragment>() + } +} diff --git a/Corona-Warn-App/src/androidTest/java/testhelpers/LocaleRule.kt b/Corona-Warn-App/src/androidTest/java/testhelpers/LocaleRule.kt new file mode 100644 index 0000000000000000000000000000000000000000..25ae728981d39dcdcc39b1510890f86b6f0aecc6 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/testhelpers/LocaleRule.kt @@ -0,0 +1,40 @@ +package testhelpers + +import android.content.res.Configuration +import android.content.res.Resources +import android.util.DisplayMetrics +import androidx.test.platform.app.InstrumentationRegistry + +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement +import java.util.Locale + +// TODO Check if it is working on different Android versions +class LocaleRule(private val locales: Array<Locale>) : TestRule { + override fun apply(base: Statement, description: Description): Statement { + return object : Statement() { + @Throws(Throwable::class) + override fun evaluate() { + val deviceLocale = Locale.getDefault() + try { + for (locale in locales) { + setLocale(locale) + base.evaluate() + } + } finally { + setLocale(deviceLocale) + } + } + } + } + + private fun setLocale(locale: Locale) { + val resources: Resources = InstrumentationRegistry.getInstrumentation().targetContext.resources + Locale.setDefault(locale) + val config: Configuration = resources.configuration + config.setLocale(locale) + val displayMetrics: DisplayMetrics = resources.displayMetrics + resources.updateConfiguration(config, displayMetrics) + } +} diff --git a/Corona-Warn-App/src/androidTest/java/testhelpers/ScreenshotCatcher.kt b/Corona-Warn-App/src/androidTest/java/testhelpers/ScreenshotCatcher.kt new file mode 100644 index 0000000000000000000000000000000000000000..7ed8ab27eef293baf3fd5efb4e610dc11162acd0 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/testhelpers/ScreenshotCatcher.kt @@ -0,0 +1,51 @@ +package testhelpers + +import android.graphics.Bitmap +import android.os.Bundle +import androidx.annotation.StyleRes +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentFactory +import androidx.fragment.app.testing.FragmentScenario +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.runner.screenshot.Screenshot +import de.rki.coronawarnapp.R + +import timber.log.Timber +import java.io.IOException +import java.util.Locale + +/** + * Captures a screenshot for the required fragment. + * it launches Fragment in container using `launchFragmentInContainer` + * then captures the screenshot in `FragmentScenario.onFragment` + * to avoid capturing a blank screenshot + */ +inline fun <reified F : Fragment> captureScreenshot( + fragmentArgs: Bundle? = null, + @StyleRes themeResId: Int = R.style.AppTheme, + factory: FragmentFactory? = null +): FragmentScenario<F> { + val launchFragmentInContainer = launchFragmentInContainer<F>(fragmentArgs, themeResId, factory) + launchFragmentInContainer.onFragment { + val language = Locale.getDefault().language + capture("${F::class.simpleName}-$language") + } + return launchFragmentInContainer +} + +/** + * Captures a screenshot. + * @param screenshotName [String] + */ +fun capture(screenshotName: String) { + + try { + Screenshot.capture().apply { + format = Bitmap.CompressFormat.PNG + name = screenshotName + process() + } + } catch (e: IOException) { + Timber.e(e) + } +}