Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: configure automatic screenshots #2517

Draft
wants to merge 1 commit into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/actions/screenshot-android/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: "Android Screenshots Workflow"

inputs:
ANDROID_EMULATOR_API:
description: 'Emulator API to be used when running tests'
required: false
default: 34
ANDROID_EMULATOR_ARCH:
description: 'Emulator architecture to be used when running tests'
required: false
default: x86_64

runs:
using: "composite"
steps:
- name: Enable KVM group perms
shell: bash
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Cache AVD
uses: actions/cache@v4
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-${{ inputs.ANDROID_EMULATOR_API }}-${{ inputs.ANDROID_EMULATOR_ARCH }}

- name: Create AVD and Cache Snapshot
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ inputs.ANDROID_EMULATOR_API }}
arch: ${{ inputs.ANDROID_EMULATOR_ARCH }}
profile: pixel_6
avd-name: pixel_6
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: Create Android Screenshots
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ inputs.ANDROID_EMULATOR_API }}
arch: ${{ inputs.ANDROID_EMULATOR_ARCH }}
profile: pixel_6
avd-name: pixel_6
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: |
bundle exec fastlane screengrab
if [[ $? -ne 0 ]]; then
exit 1
fi
13 changes: 10 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,27 @@ jobs:
read -r version_code < versionCode.txt
echo "VERSION_CODE=$version_code" >> $GITHUB_OUTPUT

- name: Add Changelogs to fastlane branch
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"

git clone --branch=fastlane --depth=1 https://${{ github.repository_owner }}:${{ github.token }}@github.com/${{ github.repository }} fastlane

- name: Android Screenshot Workflow
uses: ./.github/actions/screenshot-android
with:
ANDROID_EMULATOR_API: ${{ env.ANDROID_EMULATOR_API }}
ANDROID_EMULATOR_ARCH: ${{ env.ANDROID_EMULATOR_ARCH }}

- name: Add Changelogs to fastlane branch
run: |
cd fastlane

echo "${{ github.event.release.body }}" > metadata/android/en-US/changelogs/${{ steps.download-assets.outputs.VERSION_CODE }}.txt

# Force push to fastlane branch
git checkout --orphan temporary
git add --all .
git commit -am "[Auto] Add changelogs for versionCode: ${{ steps.download-assets.outputs.VERSION_CODE }} ($(date +%Y-%m-%d.%H:%M:%S))"
git commit -am "[Auto] Update metadata for versionCode: ${{ steps.download-assets.outputs.VERSION_CODE }} ($(date +%Y-%m-%d.%H:%M:%S))"
git branch -D fastlane
git branch -m fastlane
git push --force origin fastlane
Expand Down
7 changes: 7 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ android {
versionCode = System.getenv("VERSION_CODE")?.toInt() ?: 1
versionName = System.getenv("VERSION_NAME") ?: "1.0.0"
resConfigs("en","ru","ar","si","pl")
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

signingConfigs {
Expand Down Expand Up @@ -97,6 +98,12 @@ dependencies {

// OKHTTP
implementation("com.squareup.okhttp3:okhttp:4.12.0")
androidTestImplementation("tools.fastlane:screengrab:2.1.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.6.1")
androidTestImplementation("androidx.test:rules:1.6.1")

// ButterKnife
val butterKnifeVersion = "10.2.3"
Expand Down
173 changes: 173 additions & 0 deletions app/src/androidTest/java/io/pslab/activity/ScreenshotsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package io.pslab.activity;


import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.*;
import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
import static androidx.test.espresso.matcher.ViewMatchers.*;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;

import androidx.test.espresso.Espresso;
import androidx.test.espresso.IdlingResource;
import androidx.test.espresso.PerformException;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.util.HumanReadables;
import androidx.test.espresso.util.TreeIterables;
import androidx.test.ext.junit.rules.ActivityScenarioRule;

import tools.fastlane.screengrab.FalconScreenshotStrategy;
import tools.fastlane.screengrab.Screengrab;
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy;
import tools.fastlane.screengrab.locale.LocaleTestRule;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.GrantPermissionRule;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject;
import androidx.test.uiautomator.UiObjectNotFoundException;
import androidx.test.uiautomator.UiScrollable;
import androidx.test.uiautomator.UiSelector;
import androidx.test.uiautomator.Until;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;
import java.util.concurrent.TimeoutException;

import io.pslab.R;

@LargeTest
@RunWith(AndroidJUnit4.class)
public class ScreenshotsTest {
private static final int LAUNCH_TIMEOUT = 10000;
private static final String APP_PACKAGE_NAME = "io.pslab";
UiDevice mDevice;

@ClassRule
public static final LocaleTestRule localeTestRule = new LocaleTestRule();

@Rule
public ActivityScenarioRule<MainActivity> mActivityScenarioRule =
new ActivityScenarioRule<>(MainActivity.class);

@Rule
public GrantPermissionRule mGrantPermissionRule =
GrantPermissionRule.grant(
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.RECORD_AUDIO",
"android.permission.WRITE_EXTERNAL_STORAGE");

@Before
public void setUp() {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
mDevice = UiDevice.getInstance(instrumentation);
mDevice.pressHome();

final String launcherPackage = getLauncherPackageName();
assertThat(launcherPackage, notNullValue());

Context context = getApplicationContext();
final Intent intent = context.getPackageManager().getLaunchIntentForPackage(APP_PACKAGE_NAME);
assert intent != null;
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
Screengrab.setDefaultScreenshotStrategy(new UiAutomatorScreenshotStrategy());

mDevice.wait(Until.hasObject(By.pkg(APP_PACKAGE_NAME).depth(0)), LAUNCH_TIMEOUT);
}

private String getLauncherPackageName() {
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);

PackageManager pm = getApplicationContext().getPackageManager();
ResolveInfo resolveInfo = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
assert resolveInfo != null;
return resolveInfo.activityInfo.packageName;
}

@Test
public void testTakeScreenshot() throws UiObjectNotFoundException {
Screengrab.screenshot("dashboard");

UiObject openDrawer = mDevice.findObject(new UiSelector().description("open_drawer"));
openDrawer.click();
Screengrab.screenshot("drawer");

UiScrollable navRecyclerView = new UiScrollable(new UiSelector().resourceId(APP_PACKAGE_NAME + ":id/nav_instruments"));
UiObject item = navRecyclerView.getChild(new UiSelector().text("Instruments"));
item.click();

UiScrollable applicationsRecyclerView = new UiScrollable(new UiSelector().resourceId(APP_PACKAGE_NAME + ":id/applications_recycler_view"));
applicationsRecyclerView.scrollTextIntoView("ACCELEROMETER");
item = applicationsRecyclerView.getChild(new UiSelector().text("ACCELEROMETER"));
item.clickAndWaitForNewWindow();
Screengrab.screenshot("instrument_accelerometer_view");

mDevice.pressBack();

applicationsRecyclerView.scrollTextIntoView("BAROMETER");
item = applicationsRecyclerView.getChild(new UiSelector().text("BAROMETER"));
item.clickAndWaitForNewWindow();
Screengrab.screenshot("instrument_barometer_view");

mDevice.pressBack();

applicationsRecyclerView.scrollTextIntoView("MULTIMETER");
item = applicationsRecyclerView.getChild(new UiSelector().text("MULTIMETER"));
item.clickAndWaitForNewWindow();
Screengrab.screenshot("instrument_multimeter_view");

mDevice.pressBack();

applicationsRecyclerView.scrollTextIntoView("LOGIC ANALYZER");
item = applicationsRecyclerView.getChild(new UiSelector().text("LOGIC ANALYZER"));
item.clickAndWaitForNewWindow();
Screengrab.screenshot("logic_analyzer_view");

mDevice.pressBack();

UiObject moreOptions = mDevice.findObject(new UiSelector().description("More options"));
moreOptions.click();

UiObject pinLayoutFront = mDevice.findObject(new UiSelector().text("Pin Layout Front"));
pinLayoutFront.clickAndWaitForNewWindow();
Screengrab.screenshot("layout_pin_front");

openDrawer.click();

item = navRecyclerView.getChild(new UiSelector().text("Instruments"));
item.clickAndWaitForNewWindow();

applicationsRecyclerView.scrollTextIntoView("OSCILLOSCOPE");
item = applicationsRecyclerView.getChild(new UiSelector().text("OSCILLOSCOPE"));
item.clickAndWaitForNewWindow();
Screengrab.screenshot("oscilloscope_channel_view");

mDevice.pressBack();
}
}
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"
tools:ignore="HighSamplingRate" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>
<uses-permission android:name="android.permission.DUMP"/>

<uses-feature
android:name="android.hardware.sensor.light"
Expand Down
Loading