Browse Source

Added instrumented tests

sirekanian 2 years ago
parent
commit
9ff28b96ca

+ 18 - 0
.github/workflows/build.yml

@@ -26,3 +26,21 @@ jobs:
           files: |
             app/build/outputs/apk/release/*-release*.apk
             app/build/outputs/bundle/release/*-release.aab
+  tests:
+    runs-on: macos-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-java@v3
+        with:
+          distribution: 'temurin'
+          java-version: '11'
+      - shell: bash
+        run: ./emulator
+      - shell: bash
+        env:
+          TEST_TOKEN: ${{ secrets.KNIGOPIS_TEST_TOKEN }}
+        run: ./gradlew connectedAndroidTest
+      - if: ${{ always() }}
+        uses: actions/upload-artifact@v3
+        with:
+          path: app/build/reports/androidTests/connected/*.html

+ 1 - 0
.gitignore

@@ -6,3 +6,4 @@
 /build
 /captures
 .externalNativeBuild
+/tmp

+ 9 - 0
app/build.gradle.kts

@@ -21,6 +21,10 @@ android {
             buildConfigField("String", key, "\"$value\"")
         }
         vectorDrawables.useSupportLibrary = true
+        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+        System.getenv("TEST_TOKEN")?.let {
+            testInstrumentationRunnerArguments.put("testToken", it)
+        }
     }
     buildTypes {
         getByName("release") {
@@ -75,6 +79,11 @@ dependencies {
 
     // todo: crash reporting
     // implementation("ch.acra:acra-http:5.9.7")
+
+    // tests
+    androidTestImplementation("androidx.test.ext:junit:1.1.5")
+    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
+    androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
 }
 
 task("updateReadme") {

+ 60 - 0
app/src/androidTest/java/org/sirekanyan/knigopis/AndroidTest.kt

@@ -0,0 +1,60 @@
+package org.sirekanyan.knigopis
+
+import android.content.ComponentName
+import android.content.Intent
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.*
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val PACKAGE = "org.sirekanyan.knigopis.debug"
+private const val ACTIVITY = "org.sirekanyan.knigopis.feature.MainActivity"
+
+@RunWith(AndroidJUnit4::class)
+class AndroidTest {
+
+    private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+    private val app = ApplicationProvider.getApplicationContext<App>().also {
+        val token = InstrumentationRegistry.getArguments().getString("testToken")
+        it.tokenStorage.accessToken = checkNotNull(token) { "Test token is not specified" }
+    }
+    private val intent =
+        Intent.makeMainActivity(ComponentName(PACKAGE, ACTIVITY))
+            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+
+    @Test
+    fun addBook() {
+        app.startActivity(intent)
+        text("You don't have any books")
+        id("addBookButton").click()
+        text("Title").text = "My Book"
+        id("option_save_book").click()
+        text("Books")
+        text("My Book")
+        text("(no author)")
+    }
+
+    @Test
+    fun deleteBook() {
+        app.startActivity(intent)
+        text("My Book").click(1000)
+        text("Delete").click()
+        text("DELETE").click()
+        text("You don't have any books")
+    }
+
+    private fun id(id: String): UiObject2 =
+        wait(By.res("$PACKAGE:id/$id"))
+
+    private fun text(text: String): UiObject2 =
+        wait(By.text(text))
+
+    private fun wait(selector: BySelector): UiObject2 {
+        val condition = Until.hasObject(selector)
+        check(device.wait(condition, 60000)) { "Timeout reached" }
+        return device.findObject(selector)
+    }
+
+}

+ 68 - 0
emulator

@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+export ANDROID_HOME="tmp/AndroidSdk"
+export ANDROID_AVD_HOME="tmp/AndroidAvd"
+export ANDROID_EMULATOR_HOME="tmp/AndroidEmu"
+
+mkdir -p "$ANDROID_HOME"
+mkdir -p "$ANDROID_AVD_HOME"
+mkdir -p "$ANDROID_EMULATOR_HOME"
+
+case "$(uname -s)" in
+  Linux*) machine=linux ;;
+  *) machine=mac ;;
+esac
+
+download() {
+  output="tmp/$(basename "$2")"
+  if [ ! -f "$output" ]; then
+    wget "$2" -O "$output"
+  fi
+  eval $1="$output"
+}
+
+download cmdtools "https://dl.google.com/android/repository/commandlinetools-$machine-9477386_latest.zip"
+download firefox "https://github.com/mozilla-mobile/firefox-android/releases/download/fenix-v111.1.1/fenix-111.1.1-x86_64.apk"
+
+TOOLS="$ANDROID_HOME/cmdline-tools/latest"
+if [ ! -d "$TOOLS" ]; then
+  unzip -oq "$cmdtools"
+  mkdir -p "$ANDROID_HOME/cmdline-tools"
+  mv "cmdline-tools" "$TOOLS"
+fi
+
+SDKM="$TOOLS/bin/sdkmanager"
+AVDM="$TOOLS/bin/avdmanager"
+ADB="$ANDROID_HOME/platform-tools/adb"
+EMU="$ANDROID_HOME/emulator/emulator -no-audio -no-snapshot -gpu swiftshader_indirect -no-boot-anim"
+
+if [ "$1" != "window" ]; then
+  EMU="$EMU -no-window"
+fi
+
+if [ "$1" = "stop" ]; then
+  killall qemu-system-x86_64 qemu-system-x86_64-headless || true
+  $AVDM delete avd -n "my_emulator"
+  exit 0
+fi
+
+yes | $SDKM --licenses || true
+PACKAGE="system-images;android-31;default;x86_64"
+$SDKM "platform-tools" "platforms;android-31" "$PACKAGE"
+$AVDM create avd -f -n "my_emulator" -b "default/x86_64" -k "$PACKAGE" -d "pixel_3a"
+grep -q 'Boot completed' <($EMU -avd "my_emulator")
+echo "Boot completed!"
+
+while [ -z "$($ADB devices | grep emulator | grep device)" ]; do
+  $ADB devices
+  sleep 5
+done
+
+$ADB devices
+sleep 5
+
+$ADB install "$firefox"
+$ADB uninstall --user 0 "org.chromium.webview_shell"