diff --git a/build.gradle b/build.gradle index 38cbc3af0f..f99139904a 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,7 @@ buildscript { classpath 'org.owasp:dependency-check-gradle:7.1.1' classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.10" classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0" + classpath "com.karumi:shot:5.14.1" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/dependencies_groups.gradle b/dependencies_groups.gradle index bcd737acc9..17bade5fca 100644 --- a/dependencies_groups.gradle +++ b/dependencies_groups.gradle @@ -57,6 +57,7 @@ ext.groups = [ 'com.facebook.flipper', 'com.facebook.fresco', 'com.facebook.infer.annotation', + 'com.facebook.testing.screenshot', 'com.facebook.soloader', 'com.facebook.stetho', 'com.facebook.yoga', @@ -97,6 +98,7 @@ ext.groups = [ 'com.ibm.icu', 'com.jakewharton.android.repackaged', 'com.jakewharton.timber', + 'com.karumi', 'com.kgurgul.flipper', 'com.linkedin.dexmaker', 'com.mapbox.mapboxsdk', diff --git a/vector/build.gradle b/vector/build.gradle index 9609296907..cc0bc06aa4 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -9,6 +9,7 @@ apply plugin: 'kotlin-kapt' apply plugin: 'com.likethesalad.stem' apply plugin: 'dagger.hilt.android.plugin' apply plugin: 'kotlinx-knit' +apply plugin: 'shot' if (project.hasProperty("coverage")) { apply plugin: 'jacoco' @@ -156,7 +157,8 @@ android { buildConfigField "String", "GIT_BRANCH_NAME", "\"${gitBranchName()}\"" buildConfigField "String", "BUILD_NUMBER", "\"${buildNumber}\"" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" +// testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "com.karumi.shot.ShotTestRunner" // Keep abiFilter for the universalApk ndk { @@ -601,5 +603,5 @@ dependencies { androidTestImplementation libs.mockk.mockkAndroid androidTestUtil libs.androidx.orchestrator debugImplementation libs.androidx.fragmentTesting - androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:1.7.10" + androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:1.6.10" } diff --git a/vector/screenshots/gplay/debug/im.vector.app.screenshot.MvRxScreenshotTest_anotherRoomProfileFragmentScreenshotTest.png b/vector/screenshots/gplay/debug/im.vector.app.screenshot.MvRxScreenshotTest_anotherRoomProfileFragmentScreenshotTest.png new file mode 100644 index 0000000000..8c672a47d5 Binary files /dev/null and b/vector/screenshots/gplay/debug/im.vector.app.screenshot.MvRxScreenshotTest_anotherRoomProfileFragmentScreenshotTest.png differ diff --git a/vector/screenshots/gplay/debug/im.vector.app.screenshot.MvRxScreenshotTest_defaultRoomProfileFragmentScreenshotTest.png b/vector/screenshots/gplay/debug/im.vector.app.screenshot.MvRxScreenshotTest_defaultRoomProfileFragmentScreenshotTest.png new file mode 100644 index 0000000000..6ccf31b8d7 Binary files /dev/null and b/vector/screenshots/gplay/debug/im.vector.app.screenshot.MvRxScreenshotTest_defaultRoomProfileFragmentScreenshotTest.png differ diff --git a/vector/src/androidTest/java/im/vector/app/screenshot/MvRxScreenshotTest.kt b/vector/src/androidTest/java/im/vector/app/screenshot/MvRxScreenshotTest.kt new file mode 100644 index 0000000000..a4d50a059d --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/screenshot/MvRxScreenshotTest.kt @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.screenshot + +import android.os.Bundle +import androidx.test.core.app.ActivityScenario +import androidx.test.platform.app.InstrumentationRegistry +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.launcher.MavericksLauncherMockActivity +import com.airbnb.mvrx.mocking.MockableMavericksView +import com.airbnb.mvrx.mocking.getMockVariants +import com.airbnb.mvrx.mocking.mockSingleViewModel +import com.karumi.shot.ActivityScenarioUtils.waitForActivity +import com.karumi.shot.ScreenshotTest +import im.vector.app.features.roomprofile.RoomProfileFragment +import im.vector.app.features.roomprofile.mocks.mockRoomProfileArgs +import im.vector.app.features.roomprofile.mocks.mockRoomProfileViewState +import org.junit.Test +import kotlin.reflect.KClass + +class MvRxScreenshotTest : ScreenshotTest { + + private val context = InstrumentationRegistry.getInstrumentation().targetContext + + @Test + fun defaultRoomProfileFragmentScreenshotTest() { + val mock = MockRoomProfileFragment::class.getMock("Default state") + + val scenario = ActivityScenario.launch( + MavericksLauncherMockActivity.intent(context, mock) + ) + + compareScreenshot(scenario.waitForActivity()) + } + + @Test + fun anotherRoomProfileFragmentScreenshotTest() { + val mock = MockRoomProfileFragment::class.getMock("my-state") + + val scenario = ActivityScenario.launch( + MavericksLauncherMockActivity.intent(context, mock) + ) + + compareScreenshot(scenario.waitForActivity()) + } +} + +fun KClass.getMock(name: String) = getMockVariants(this.java)!!.first { it.mock.name == name } + +class MockRoomProfileFragment : RoomProfileFragment(), MockableMavericksView { + + override fun provideMocks() = mockSingleViewModel( + viewModelReference = MockRoomProfileFragment::roomProfileViewModel, + defaultState = mockRoomProfileViewState, + defaultArgs = mockRoomProfileArgs, + ) { + state("my-state") { + val summary = (mockRoomProfileViewState.roomSummary as Success)().copy( + displayName = "another state" + ) + mockRoomProfileViewState.copy( + roomSummary = Success(summary) + ) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + registerMockPrinter() + super.onCreate(savedInstanceState) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index 3574bf8158..12716835d1 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -29,9 +29,6 @@ import androidx.fragment.app.setFragmentResultListener import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel -import com.airbnb.mvrx.mocking.MavericksViewMocks -import com.airbnb.mvrx.mocking.MockableMavericksView -import com.airbnb.mvrx.mocking.mockSingleViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint @@ -56,8 +53,6 @@ import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel -import im.vector.app.features.roomprofile.mocks.mockRoomProfileArgs -import im.vector.app.features.roomprofile.mocks.mockRoomProfileViewState import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.parcelize.Parcelize @@ -72,9 +67,8 @@ data class RoomProfileArgs( ) : Parcelable @AndroidEntryPoint -class RoomProfileFragment : +open class RoomProfileFragment : VectorBaseFragment(), - MockableMavericksView, RoomProfileController.Callback, VectorMenuProvider { @@ -87,16 +81,10 @@ class RoomProfileFragment : private val roomProfileArgs: RoomProfileArgs by args() private lateinit var roomListQuickActionsSharedActionViewModel: RoomListQuickActionsSharedActionViewModel private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel - private val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel() + val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel() private var appBarStateChangeListener: AppBarStateChangeListener? = null - override fun provideMocks() = mockSingleViewModel( - viewModelReference = RoomProfileFragment::roomProfileViewModel, - defaultState = mockRoomProfileViewState, - defaultArgs = mockRoomProfileArgs - ){} - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentMatrixProfileBinding { return FragmentMatrixProfileBinding.inflate(inflater, container, false) } @@ -105,7 +93,6 @@ class RoomProfileFragment : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - registerMockPrinter() analyticsScreenName = MobileScreen.ScreenName.RoomDetails setFragmentResultListener(MigrateRoomBottomSheet.REQUEST_KEY) { _, bundle -> bundle.getString(MigrateRoomBottomSheet.BUNDLE_KEY_REPLACEMENT_ROOM)?.let { replacementRoomId -> diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/mocks/RoomProfileViewStateMock.kt b/vector/src/main/java/im/vector/app/features/roomprofile/mocks/RoomProfileViewStateMock.kt index de207c8b48..a4c807cad6 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/mocks/RoomProfileViewStateMock.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/mocks/RoomProfileViewStateMock.kt @@ -19,65 +19,75 @@ import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent import org.matrix.android.sdk.api.session.room.sender.SenderInfo import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent -val mockRoomProfileViewState by lazy { RoomProfileViewState( -roomId = "!CWLUCoEWXSFyTCOtfL:matrix.org", -roomSummary = Success( -value = RoomSummary( -roomId = "!CWLUCoEWXSFyTCOtfL:matrix.org", -displayName = "Megolm test (E2E Encryption Testfest)", -name = "Megolm test (E2E Encryption Testfest)", -topic = "Test room! Please discuss issues at #e2e:matrix.org. Requires synapse 0.18.0 or later. (0.18.4 for encrypted images); Latest version is recommended.", -avatarUrl = "", -canonicalAlias = "#megolm:matrix.org", -joinRules = RoomJoinRules.PUBLIC, -joinedMembersCount = 199, -latestPreviewableEvent = TimelineEvent( -root = Event( -type = "m.room.encrypted", -eventId = "\$zo2MIeXtdl9dWNhz1cbY3YT55rlpQLG8XpYBTFN-JXI", -content = mutableMapOf("algorithm" to "m.megolm.v1.aes-sha2","ciphertext" to "AwgAEoADprWrq3ZJ4BLD/w0+Lx9H/3mXZpnEQdgbF2FoWOPB1fuki6/1w7vikL1Sb6rP5AwMuA4TPJQ14Pl++/TCGCUzRoZE9m8TsYe0nQXKb2VWRDB6u7i1ib27l3epbtQAIOYfKuCg6MnbMmadef9fEA1K4lHl3xxf0SdWF9PQKGq3nf9rhcZwa3AZW+FDepI094NO98bTHmssKJ5YTp1ChStjALy4FwdcfanlD5WWPHbKqij/RIC63FNAspIOUeb3AkBmcAdoUctBrxXml+NpJKKCGKxXvFudeSkiqHMG","device_id" to "QBOXOIATIX","sender_key" to "dGWOgnrcFYOdsYAfwvKJQcDr4dwuZ0R743B3ZlGSMWU","session_id" to "Eg1c1yyhczbhGt2baH4m6qI4TpJxN0Kb59TxZ3JY5lE"), -originServerTs = 1661232614808, -senderId = "@DatseMultimedia:matrix.org", -roomId = "!CWLUCoEWXSFyTCOtfL:matrix.org", -unsignedData = UnsignedData(age = 7112957) -), -localId = 133, -eventId = "\$zo2MIeXtdl9dWNhz1cbY3YT55rlpQLG8XpYBTFN-JXI", -displayIndex = 11, -senderInfo = SenderInfo( -userId = "@DatseMultimedia:matrix.org", -displayName = "Jigme Datse (they/them)", -isUniqueDisplayName = true, -avatarUrl = "mxc://matrix.org/aIPbYElyzjKrOWPaWkxbWEqy" -) -), -otherMemberIds = listOf( -"@zocker1999:matrix.org", -"@denisea:element.io", -"@+:jae.fi" -), -membership = Membership.JOIN, -versioningState = VersioningState.NONE, -readMarkerId = "\$-wLehwJjk4gGG8igOfp-giNzGQruvlZMI-Vti1YYxHA", -isEncrypted = true, -encryptionEventTs = 1574382534883, -typingUsers = listOf( +val mockRoomProfileViewState by lazy { + RoomProfileViewState( + roomId = "!CWLUCoEWXSFyTCOtfL:matrix.org", + roomSummary = Success( + value = RoomSummary( + roomId = "!CWLUCoEWXSFyTCOtfL:matrix.org", + displayName = "Megolm test (E2E Encryption Testfest)", + name = "Megolm test (E2E Encryption Testfest)", + topic = "Test room! Please discuss issues at #e2e:matrix.org. Requires synapse 0.18.0 or later. (0.18.4 for encrypted images); Latest version is recommended.", + avatarUrl = "", + canonicalAlias = "#megolm:matrix.org", + joinRules = RoomJoinRules.PUBLIC, + joinedMembersCount = 199, + latestPreviewableEvent = TimelineEvent( + root = Event( + type = "m.room.encrypted", + eventId = "\$zo2MIeXtdl9dWNhz1cbY3YT55rlpQLG8XpYBTFN-JXI", + content = mutableMapOf( + "algorithm" to "m.megolm.v1.aes-sha2", + "ciphertext" to "AwgAEoADprWrq3ZJ4BLD/w0+Lx9H/3mXZpnEQdgbF2FoWOPB1fuki6/1w7vikL1Sb6rP5AwMuA4TPJQ14Pl++/TCGCUzRoZE9m8TsYe0nQXKb2VWRDB6u7i1ib27l3epbtQAIOYfKuCg6MnbMmadef9fEA1K4lHl3xxf0SdWF9PQKGq3nf9rhcZwa3AZW+FDepI094NO98bTHmssKJ5YTp1ChStjALy4FwdcfanlD5WWPHbKqij/RIC63FNAspIOUeb3AkBmcAdoUctBrxXml+NpJKKCGKxXvFudeSkiqHMG", + "device_id" to "QBOXOIATIX", + "sender_key" to "dGWOgnrcFYOdsYAfwvKJQcDr4dwuZ0R743B3ZlGSMWU", + "session_id" to "Eg1c1yyhczbhGt2baH4m6qI4TpJxN0Kb59TxZ3JY5lE" + ), + originServerTs = 1661232614808, + senderId = "@DatseMultimedia:matrix.org", + roomId = "!CWLUCoEWXSFyTCOtfL:matrix.org", + unsignedData = UnsignedData(age = 7112957) + ), + localId = 133, + eventId = "\$zo2MIeXtdl9dWNhz1cbY3YT55rlpQLG8XpYBTFN-JXI", + displayIndex = 11, + senderInfo = SenderInfo( + userId = "@DatseMultimedia:matrix.org", + displayName = "Jigme Datse (they/them)", + isUniqueDisplayName = true, + avatarUrl = "mxc://matrix.org/aIPbYElyzjKrOWPaWkxbWEqy" + ) + ), + otherMemberIds = listOf( + "@zocker1999:matrix.org", + "@denisea:element.io", + "@+:jae.fi" + ), + membership = Membership.JOIN, + versioningState = VersioningState.NONE, + readMarkerId = "\$-wLehwJjk4gGG8igOfp-giNzGQruvlZMI-Vti1YYxHA", + isEncrypted = true, + encryptionEventTs = 1574382534883, + typingUsers = listOf( -), -roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Default, -roomEncryptionAlgorithm = RoomEncryptionAlgorithm.Megolm -) -), -roomCreateContent = Success( -value = RoomCreateContent( -creator = "@abuse:matrix.org", -roomVersion = "5", -predecessor = Predecessor(roomId = "!UCnwUWwIKhcpaPTHtR:sw1v.org",eventId = "\$1574382534206ivbfT:matrix.org") -) -), -bannedMembership = Success(value = listOf( + ), + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Default, + roomEncryptionAlgorithm = RoomEncryptionAlgorithm.Megolm + ) + ), + roomCreateContent = Success( + value = RoomCreateContent( + creator = "@abuse:matrix.org", + roomVersion = "5", + predecessor = Predecessor(roomId = "!UCnwUWwIKhcpaPTHtR:sw1v.org", eventId = "\$1574382534206ivbfT:matrix.org") + ) + ), + bannedMembership = Success( + value = listOf( -)), -actionPermissions = RoomProfileViewState.ActionPermissions(), -recommendedRoomVersion = "9" -) } + ) + ), + actionPermissions = RoomProfileViewState.ActionPermissions(), + recommendedRoomVersion = "9" + ) +}