From 2198fdb9508566309e2921dce33595e95d978479 Mon Sep 17 00:00:00 2001 From: Jakub Zerko Date: Fri, 18 Oct 2024 07:50:06 +0200 Subject: [PATCH] feat: screenshot tests --- .gitattributes | 1 + .github/workflows/generate-screenshots.yml | 99 +++++++++++++++++++ app/build.gradle.kts | 11 +++ .../messages/item/PreviewMessageTypes.kt} | 13 +-- .../messages/item/PreviewSystemMessageItem.kt | 3 +- gradle.properties | 1 + gradle/libs.versions.toml | 2 + settings.gradle.kts | 1 + 8 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/generate-screenshots.yml rename app/src/{main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageTypesPreview.kt => screenshotTest/kotlin/com/wire/android/kotlin/ui/home/conversations/messages/item/PreviewMessageTypes.kt} (97%) rename app/src/{main/kotlin/com/wire/android => screenshotTest/kotlin/com/wire/android/kotlin}/ui/home/conversations/messages/item/PreviewSystemMessageItem.kt (98%) diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..530bea6e78f --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +app/src/internalDebug/screenshotTests/** filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/generate-screenshots.yml b/.github/workflows/generate-screenshots.yml new file mode 100644 index 00000000000..ba99bcc687a --- /dev/null +++ b/.github/workflows/generate-screenshots.yml @@ -0,0 +1,99 @@ +name: "Generate and Verify Screenshot Tests" + +on: + workflow_dispatch: + push: + branches: + - feat/screenshot-tests + +permissions: + contents: write + pull-requests: write + +env: + GITHUB_TOKEN: ${{ secrets.ANDROID_BOB_GH_TOKEN }} + +jobs: + generate-screenshots: + runs-on: buildjet-8vcpu-ubuntu-2204 + + if: github.ref == 'refs/heads/feat/screenshot-tests' + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: Set up JDK 17 + uses: buildjet/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle + + - name: Validate Gradle wrapper + uses: gradle/actions/wrapper-validation@v4 + + - name: Fetch All Branches + run: git fetch --all + + - name: Merge Develop into screenshot-tests + run: | + git checkout screenshot-tests + git merge origin/develop --no-edit + git push origin screenshot-tests + + - name: Verify Screenshot Tests + run: ./gradlew validateInternalDebugScreenshotTest + + - name: Update Screenshot Reference Images + run: ./gradlew updateInternalDebugScreenshotTest + + - name: Zip Screenshot Test Reports + run: | + zip -r screenshot-test-report.zip build/reports/screenshotTest/preview/debug/internal/* + + - name: Upload Screenshot Test Report + uses: actions/upload-artifact@v4 + with: + name: screenshot-test-report + path: screenshot-test-report.zip + + - name: Create New Branch + run: | + BRANCH_NAME="test/screenshots-update-$(date +%Y%m%d%H%M%S)" + git checkout -b $BRANCH_NAME + git add . + git commit -m "test: update screenshot tests" + git push origin $BRANCH_NAME + + - name: Create Pull Request + id: create_pr + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ env.GITHUB_TOKEN }} + branch: $BRANCH_NAME + base: screenshot-tests + title: "Update Screenshot Tests" + body: "Automated PR to update screenshot tests." + + - name: Add Comment with Test Report Link + uses: actions/github-script@v6 + with: + github-token: ${{ env.GITHUB_TOKEN }} + script: | + const prNumber = ${{ steps.create_pr.outputs.pull_request_number }}; + const artifactUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts`; + github.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: `Screenshot test results have been generated. [Download the report](artifactUrl).` + }); + + - name: Cleanup Gradle Cache + run: | + rm -f ~/.gradle/caches/modules-2/modules-2.lock + rm -f ~/.gradle/caches/modules-2/gc.properties diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 08f044e4fab..e7f655796fd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -38,6 +38,7 @@ plugins { id(ScriptPlugins.testing) id(libs.plugins.wire.kover.get().pluginId) id(libs.plugins.wire.versionizer.get().pluginId) + alias(libs.plugins.screenshot) } repositories { @@ -82,6 +83,13 @@ android { jniLibs.pickFirsts.add("**/libsodium.so") } android.buildFeatures.buildConfig = true + experimentalProperties["android.experimental.enableScreenshotTest"] = true + + testOptions { + screenshotTests { + imageDifferenceThreshold = 0.0001f // 0.01% + } + } sourceSets { allFlavors.forEach { flavor -> @@ -240,6 +248,9 @@ dependencies { implementation(libs.aboutLibraries.ui) implementation(libs.compose.qr.code) + // screenshot testing + screenshotTestImplementation(libs.compose.ui.tooling) + // Unit/Android tests dependencies testImplementation(libs.androidx.test.archCore) testImplementation(libs.junit4) // Maybe migrate completely to Junit 5? diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageTypesPreview.kt b/app/src/screenshotTest/kotlin/com/wire/android/kotlin/ui/home/conversations/messages/item/PreviewMessageTypes.kt similarity index 97% rename from app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageTypesPreview.kt rename to app/src/screenshotTest/kotlin/com/wire/android/kotlin/ui/home/conversations/messages/item/PreviewMessageTypes.kt index 1eaaa0d43d9..cee0d9dd6bc 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageTypesPreview.kt +++ b/app/src/screenshotTest/kotlin/com/wire/android/kotlin/ui/home/conversations/messages/item/PreviewMessageTypes.kt @@ -16,9 +16,7 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -@file:Suppress("TooManyFunctions") - -package com.wire.android.ui.home.conversations.messages.item +package com.wire.android.kotlin.ui.home.conversations.messages.item import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable @@ -27,6 +25,9 @@ import com.wire.android.media.audiomessage.AudioMediaPlayingState import com.wire.android.media.audiomessage.AudioState import com.wire.android.model.Clickable import com.wire.android.ui.home.conversations.info.ConversationDetailsData +import com.wire.android.ui.home.conversations.messages.item.MessageClickActions +import com.wire.android.ui.home.conversations.messages.item.RegularMessageItem +import com.wire.android.ui.home.conversations.messages.item.SystemMessageItem import com.wire.android.ui.home.conversations.mock.mockAssetAudioMessage import com.wire.android.ui.home.conversations.mock.mockAssetMessage import com.wire.android.ui.home.conversations.mock.mockFooter @@ -336,15 +337,15 @@ fun PreviewMessageWithSystemMessage() { SystemMessageItem( mockMessageWithKnock.copy( messageContent = UIMessageContent.SystemMessage.MissedCall.YouCalled( - UIText.DynamicString("You") + UIText.DynamicString("ME") ) ) ) SystemMessageItem( mockMessageWithKnock.copy( messageContent = UIMessageContent.SystemMessage.MemberAdded( - UIText.DynamicString("You"), - listOf(UIText.DynamicString("Adam Smith")) + UIText.DynamicString("ME"), + listOf(UIText.DynamicString("John Smith")) ) ) ) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/PreviewSystemMessageItem.kt b/app/src/screenshotTest/kotlin/com/wire/android/kotlin/ui/home/conversations/messages/item/PreviewSystemMessageItem.kt similarity index 98% rename from app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/PreviewSystemMessageItem.kt rename to app/src/screenshotTest/kotlin/com/wire/android/kotlin/ui/home/conversations/messages/item/PreviewSystemMessageItem.kt index def7bd98fb9..7cfc46ac012 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/PreviewSystemMessageItem.kt +++ b/app/src/screenshotTest/kotlin/com/wire/android/kotlin/ui/home/conversations/messages/item/PreviewSystemMessageItem.kt @@ -17,9 +17,10 @@ */ @file:Suppress("TooManyFunctions") -package com.wire.android.ui.home.conversations.messages.item +package com.wire.android.kotlin.ui.home.conversations.messages.item import androidx.compose.runtime.Composable +import com.wire.android.ui.home.conversations.messages.item.SystemMessageItem import com.wire.android.ui.home.conversations.mock.mockMessageWithKnock import com.wire.android.ui.home.conversations.mock.mockUsersUITexts import com.wire.android.ui.home.conversations.model.UIMessageContent diff --git a/gradle.properties b/gradle.properties index 9201c2b69f5..c0b025e3111 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,3 +23,4 @@ kotlin.code.style=official # Support KMP Gradle Composite Builds - See https://youtrack.jetbrains.com/issue/KT-52172/ kotlin.mpp.import.enableKgpDependencyResolution=true org.gradle.logging.level=QUIET +android.experimental.enableScreenshotTest=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 48c67a68f3b..7f1fd5c344a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -52,6 +52,7 @@ compose-compiler = "1.5.13" compose-constraint = "1.0.1" compose-navigation = "2.7.7" # adjusted to work with compose-destinations "1.9.54" compose-destinations = "1.10.2" +screenshot = "0.0.1-alpha07" # Hilt hilt = "2.51.1" @@ -115,6 +116,7 @@ kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } aboutLibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "aboutLibraries" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +screenshot = { id = "com.android.compose.screenshot", version.ref = "screenshot"} # Home-made convention plugins defined in build-logic wire-android-application = { id = "com.wire.android.application" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 1404bba0892..4b57446af8f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,7 @@ pluginManagement { includeBuild("build-logic") repositories { mavenCentral() + google() } }