From d16c3c55e00dba1a046ef0af6b48698c5c8e37bb Mon Sep 17 00:00:00 2001
From: Mohamed Metwalli <mohamed.metwalli@sap.com>
Date: Tue, 29 Dec 2020 16:04:58 +0100
Subject: [PATCH] Add instrumentation tests to capture screenshots in
 Onboarding flow

---
 .../OnboardingFragmentScreenshot.kt           | 47 +++++++++++++++++
 ...boardingNotificationsFragmentScreenshot.kt | 46 +++++++++++++++++
 .../OnboardingPrivacyFragmentScreenshot.kt    | 46 +++++++++++++++++
 .../OnboardingTestFragmentScreenshot.kt       | 46 +++++++++++++++++
 .../OnboardingTracingFragmentScreenshot.kt    | 46 +++++++++++++++++
 .../java/testhelpers/LocaleRule.kt            | 40 +++++++++++++++
 .../java/testhelpers/ScreenshotCatcher.kt     | 51 +++++++++++++++++++
 7 files changed, 322 insertions(+)
 create mode 100644 Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingFragmentScreenshot.kt
 create mode 100644 Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingNotificationsFragmentScreenshot.kt
 create mode 100644 Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingPrivacyFragmentScreenshot.kt
 create mode 100644 Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingTestFragmentScreenshot.kt
 create mode 100644 Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/screenshot/OnboardingTracingFragmentScreenshot.kt
 create mode 100644 Corona-Warn-App/src/androidTest/java/testhelpers/LocaleRule.kt
 create mode 100644 Corona-Warn-App/src/androidTest/java/testhelpers/ScreenshotCatcher.kt

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 000000000..930cfb28b
--- /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 000000000..6b1076170
--- /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 000000000..76344f500
--- /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 000000000..30f7c497a
--- /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 000000000..768997eb1
--- /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 000000000..25ae72898
--- /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 000000000..7ed8ab27e
--- /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)
+    }
+}
-- 
GitLab