Skip to content

Commit

Permalink
implement automatic screenshot generation
Browse files Browse the repository at this point in the history
  • Loading branch information
johan12345 committed Dec 16, 2023
1 parent f6feb2c commit 8664660
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 6 deletions.
101 changes: 101 additions & 0 deletions .github/workflows/screenshots.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
on:
push:
branches:
- '*'

name: Generate Screenshots

jobs:

screenshot:
name: Generate screenshots
runs-on: macos-latest
strategy:
matrix:
api-level: [ 34 ]
steps:
- name: Check out code
uses: actions/checkout@v4

- name: AVD cache
uses: actions/cache@v3
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-${{ matrix.api-level }}

- name: create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
target: playstore
arch: x86_64
profile: pixel_4
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
script: echo "Generated AVD snapshot for caching."

- name: Set up Java environment
uses: actions/setup-java@v3
with:
java-version: 17
distribution: 'zulu'
cache: 'gradle'

- name: Build app
run: ./gradlew assembleGoogleNormalDebug assembleGoogleNormalAndroidTest
env:
GOINGELECTRIC_API_KEY: ${{ secrets.GOINGELECTRIC_API_KEY }}
OPENCHARGEMAP_API_KEY: ${{ secrets.OPENCHARGEMAP_API_KEY }}
CHARGEPRICE_API_KEY: ${{ secrets.CHARGEPRICE_API_KEY }}
MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }}
GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }}
FRONYX_API_KEY: ${{ secrets.FRONYX_API_KEY }}
ACRA_CRASHREPORT_CREDENTIALS: ${{ secrets.ACRA_CRASHREPORT_CREDENTIALS }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}
KEYSTORE_ALIAS_PASSWORD: ${{ secrets.KEYSTORE_ALIAS_PASSWORD }}

- name: Start System UI demo mode
run: |
- name: Run emulator and generate screenshots
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
target: playstore
arch: x86_64
profile: pixel_4
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
script: |
adb shell settings put global hidden_api_policy 1
# Start demo mode
adb shell settings put global sysui_demo_allowed 1
# Display time 12:00
adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm 1200
# Display full mobile data without type
adb shell am broadcast -a com.android.systemui.demo -e command network -e mobile show -e level 4 -e datatype false
adb shell am broadcast -a com.android.systemui.demo -e command network -e wifi show -e level 4 -e fully true
# Hide notifications
adb shell am broadcast -a com.android.systemui.demo -e command notifications -e visible false
# Show full battery but not in charging state
adb shell am broadcast -a com.android.systemui.demo -e command battery -e plugged false -e level 100
fastlane screengrab --app_apk_path app/build/outputs/apk/googleNormal/debug/app-google-normal-debug.apk --test_apk_path app/build/outputs/apk/androidTest/googleNormal/debug/app-google-normal-debug-androidTest.apk --tests_package_name=net.vonforst.evmap.debug.test --app_package_name net.vonforst.evmap.debug -p net.vonforst.evmap.screenshot --use_timestamp_suffix false --clear_previous_screenshots true -q en-US,de-DE
adb shell am broadcast -a com.android.systemui.demo -e command exit
- name: Upload screenshots as artifacts
uses: actions/upload-artifact@v3
with:
name: screenshots
path: fastlane/metadata/android
7 changes: 5 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ dependencies {
automotiveImplementation("androidx.car.app:app-automotive:$carAppVersion")

// AnyMaps
val anyMapsVersion = "8f1226e1c5"
val anyMapsVersion = "b27457c8f4"
implementation("com.github.ev-map.AnyMaps:anymaps-base:$anyMapsVersion")
googleImplementation("com.github.ev-map.AnyMaps:anymaps-google:$anyMapsVersion")
googleImplementation("com.google.android.gms:play-services-maps:18.2.0")
Expand Down Expand Up @@ -317,6 +317,7 @@ dependencies {
debugImplementation("com.facebook.flipper:flipper:0.238.0")
debugImplementation("com.facebook.soloader:soloader:0.10.5")
debugImplementation("com.facebook.flipper:flipper-network-plugin:0.238.0")
debugImplementation("androidx.test.espresso:espresso-idling-resource:3.5.1")

// testing
testImplementation("junit:junit:4.13.2")
Expand All @@ -327,11 +328,13 @@ dependencies {
testImplementation("androidx.test:core:1.5.0")
testImplementation("androidx.arch.core:core-testing:2.2.0")
testImplementation("androidx.car.app:app-testing:$carAppVersion")
testImplementation("androidx.test:core:1.5.0")

androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.5.1")
androidTestImplementation("androidx.test:rules:1.5.0")
androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation("tools.fastlane:screengrab:2.1.1")

kapt("com.squareup.moshi:moshi-kotlin-codegen:1.15.0")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package net.vonforst.evmap.screenshot

import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.content.Intent
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.contrib.DrawerActions
import androidx.test.espresso.contrib.NavigationViewActions
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import androidx.test.rule.GrantPermissionRule.grant
import kotlinx.coroutines.runBlocking
import net.vonforst.evmap.EXTRA_CHARGER_ID
import net.vonforst.evmap.EXTRA_LAT
import net.vonforst.evmap.EXTRA_LON
import net.vonforst.evmap.EspressoIdlingResource
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.storage.PreferenceDataSource
import org.junit.BeforeClass
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import tools.fastlane.screengrab.Screengrab
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy
import tools.fastlane.screengrab.locale.LocaleTestRule
import net.vonforst.evmap.R
import net.vonforst.evmap.api.goingelectric.GEReferenceData
import net.vonforst.evmap.api.goingelectric.GoingElectricApiWrapper
import net.vonforst.evmap.model.Favorite
import net.vonforst.evmap.storage.AppDatabase


@RunWith(AndroidJUnit4::class)
class ScreenshotTest {
companion object {
@JvmStatic
@BeforeClass
fun beforeAll() {
Screengrab.setDefaultScreenshotStrategy(UiAutomatorScreenshotStrategy())
IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource)

val context = InstrumentationRegistry.getInstrumentation().targetContext
val prefs = PreferenceDataSource(context)
prefs.dataSourceSet = true
prefs.welcomeDialogShown = true
prefs.privacyAccepted = true
prefs.opensourceDonationsDialogShown = true
prefs.chargepriceMyVehicles = setOf("b58bc94d-d929-ad71-d95b-08b877bf76ba")

// insert favorites
val db = AppDatabase.getInstance(context)
val api = GoingElectricApiWrapper(
context.getString(R.string.goingelectric_key),
context = context
)
val ids = listOf(70774L, 40315L, 49219L)
runBlocking {
val refData = api.getReferenceData().data as GEReferenceData
ids.forEachIndexed { i, id ->
val detail = api.getChargepointDetail(refData, id).data!!
db.chargeLocationsDao().insert(detail)
db.favoritesDao().insert(
Favorite(
i.toLong(),
chargerId = id,
chargerDataSource = "goingelectric"
)
)
}
}
}
}

@get:Rule
val localeTestRule = LocaleTestRule()

@get:Rule
val activityRule: ActivityScenarioRule<MapsActivity> = ActivityScenarioRule(
Intent(
InstrumentationRegistry.getInstrumentation().targetContext,
MapsActivity::class.java
).apply {
putExtra(EXTRA_CHARGER_ID, 62489L)
putExtra(EXTRA_LAT, 53.099512)
putExtra(EXTRA_LON, 9.981884)
})

@get:Rule
val permissionRule: GrantPermissionRule = grant(ACCESS_FINE_LOCATION)

@Test
fun testTakeScreenshot() {
Thread.sleep(3000L)
Screengrab.screenshot("01_map")

onView(withId(R.id.topPart)).perform(click())
Screengrab.screenshot("02_detail")

onView(withId(R.id.btnChargeprice)).perform(click())
Screengrab.screenshot("03_prices")

onView(withId(R.id.drawer_layout)).perform(DrawerActions.open())
onView(withId(R.id.nav_view)).perform(NavigationViewActions.navigateTo(R.id.favs))

Thread.sleep(3000L)
Screengrab.screenshot("04_favorites")
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.johan.evmap.storage
package net.vonforst.evmap.storage

import android.content.Context
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
Expand Down
17 changes: 16 additions & 1 deletion app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Permissions needed for fastlane screengrab -->

<!-- Allows unlocking your device and activating its screen so UI tests can succeed -->
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<!-- Allows for storing and retrieving screenshots -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<!-- Allows changing locales -->
<uses-permission
android:name="android.permission.CHANGE_CONFIGURATION"
tools:ignore="ProtectedPermissions" />

<application>
<activity
Expand Down
24 changes: 23 additions & 1 deletion app/src/debug/java/net/vonforst/evmap/DebugInits.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package net.vonforst.evmap

import android.content.Context
import android.os.Build
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.idling.CountingIdlingResource
import com.facebook.flipper.android.AndroidFlipperClient
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin
import com.facebook.flipper.plugins.inspector.DescriptorMapping
Expand Down Expand Up @@ -34,9 +36,29 @@ fun OkHttpClient.Builder.addDebugInterceptors(): OkHttpClient.Builder {
} catch (e: ClassNotFoundException) {
isRunningTest = false
}

if (!isRunningTest) {
this.addNetworkInterceptor(FlipperOkhttpInterceptor(networkFlipperPlugin))
}
return this
}

/**
* Contains a static reference to [IdlingResource], only available in the 'debug' build type.
*/
object EspressoIdlingResource {
private const val RESOURCE = "GLOBAL"

@JvmField
val countingIdlingResource = CountingIdlingResource(RESOURCE)

fun increment() {
countingIdlingResource.increment()
}

fun decrement() {
if (!countingIdlingResource.isIdleNow) {
countingIdlingResource.decrement()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import jsonapi.Relationships
import jsonapi.ResourceIdentifier
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import net.vonforst.evmap.EspressoIdlingResource
import net.vonforst.evmap.api.chargeprice.*
import net.vonforst.evmap.api.equivalentPlugTypes
import net.vonforst.evmap.model.ChargeLocation
Expand Down Expand Up @@ -48,6 +49,7 @@ class ChargepriceViewModel(
} else {
value = Resource.loading(null)
viewModelScope.launch {
EspressoIdlingResource.increment()
value = try {
val result = api.getVehicles()
Resource.success(result.filter {
Expand All @@ -57,6 +59,8 @@ class ChargepriceViewModel(
Resource.error(e.message, null)
} catch (e: HttpException) {
Resource.error(e.message, null)
} finally {
EspressoIdlingResource.decrement()
}
}
}
Expand Down Expand Up @@ -253,6 +257,7 @@ class ChargepriceViewModel(

loadPricesJob?.cancel()
loadPricesJob = viewModelScope.launch {
EspressoIdlingResource.increment()
try {
val result = api.getChargePrices(
ChargepriceRequest(
Expand Down Expand Up @@ -295,6 +300,8 @@ class ChargepriceViewModel(
} catch (e: HttpException) {
chargePrices.value = Resource.error(e.message, null)
chargePriceMeta.value = Resource.error(e.message, null)
} finally {
EspressoIdlingResource.decrement()
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion app/src/release/java/net/vonforst/evmap/DebugInits.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@ fun addDebugInterceptors(context: Context) {

}

fun OkHttpClient.Builder.addDebugInterceptors(): OkHttpClient.Builder = this
fun OkHttpClient.Builder.addDebugInterceptors(): OkHttpClient.Builder = this

object EspressoIdlingResource {
fun increment() {}

fun decrement() {}
}

0 comments on commit 8664660

Please sign in to comment.