diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fc7311f0..25216bc7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -47,7 +47,6 @@ debugTests: .connected-template: &connected-template stage: test - image: registry.gitlab.com/fdroid/ci-images-client script: - start-emulator - wait-for-emulator @@ -57,12 +56,12 @@ debugTests: - apt-get update || apt-get update - apt-get install -y openjdk-11-jdk-headless - update-alternatives --auto java - - ./gradlew connectedStagingAndroidTest || (adb -e logcat -d > logcat.txt; exit 1) + - ./gradlew connectedStagingAndroidTest --info || (adb -e logcat -d > logcat.txt; exit 1) artifacts: paths: - logcat.txt -connected 24 default x86_64: +connected 27 default x86_64: <<: *connected-template fdroid build: diff --git a/app/build.gradle b/app/build.gradle index 8ba5a6bd..cd95252a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,3 +1,5 @@ +import com.android.build.api.dsl.ManagedVirtualDevice + plugins { id "com.cookpad.android.plugin.license-tools" version "1.2.2" } @@ -31,7 +33,7 @@ android { versionCode 16 versionName "1.0.beta" + versionCode - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "org.pixeldroid.app.testUtility.TestRunner" testInstrumentationRunnerArguments clearPackageData: 'true' } sourceSets { @@ -43,10 +45,6 @@ android { testBuildType "staging" buildTypes { - debug { - applicationIdSuffix '.debug' - versionNameSuffix "-debug" - } staging { initWith debug testCoverageEnabled true @@ -69,6 +67,10 @@ android { buildConfigField "String", "CLIENT_ID", System.getenv("CLIENT_ID") ?: localProperties['CLIENT_ID'] ?: '""' buildConfigField "String", "CLIENT_SECRET", System.getenv("CLIENT_SECRET") ?: localProperties['CLIENT_SECRET'] ?: '""' } + debug { + applicationIdSuffix '.debug' + versionNameSuffix "-debug" + } release { minifyEnabled true shrinkResources true @@ -82,9 +84,24 @@ android { variant.resValue 'string', 'application_id', variant.applicationId } - testOptions { animationsDisabled true + managedDevices { + devices { + pixel2api30 (ManagedVirtualDevice) { + // Use device profiles you typically see in Android Studio. + device = "Pixel 2" + // Use only API levels 27 and higher. + apiLevel = 30 + // To include Google services, use "google". + systemImageSource = "aosp-atd" + // Whether the image must be a 64 bit image. Defaults to false, + // in which case the managed device will use a 32 bit image. + // Not applicable to arm64 machines. + require64Bit = false + } + } + } } buildFeatures { @@ -217,6 +234,10 @@ dependencies { */ //stagingImplementation 'com.squareup.leakcanary:leakcanary-android:2.7' + androidTestImplementation 'com.android.support.test.espresso:espresso-idling-resource:3.0.2' + + androidTestImplementation 'com.linkedin.testbutler:test-butler-library:2.2.1' + androidTestUtil 'com.linkedin.testbutler:test-butler-app:2.2.1' androidTestImplementation 'androidx.work:work-testing:2.7.1' testImplementation 'com.github.tomakehurst:wiremock-jre8:2.33.2' diff --git a/app/src/androidTest/java/org/pixeldroid/app/DrawerMenuTest.kt b/app/src/androidTest/java/org/pixeldroid/app/DrawerMenuTest.kt index 19b2584d..6a965423 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/DrawerMenuTest.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/DrawerMenuTest.kt @@ -108,12 +108,12 @@ class DrawerMenuTest { // Check that profile activity was opened. waitForView(R.id.editButton) onView(withId(R.id.editButton)).check(matches(isDisplayed())) - val followersText = context.resources.getQuantityString(R.plurals.nb_followers, 2, 2) + val followersText = context.resources.getQuantityString(R.plurals.nb_followers, 1, 1) waitForView(R.id.nbFollowingTextView, allOf(withId(R.id.nbFollowersTextView), withText(followersText))) onView(withText(followersText)).perform(click()) waitForView(R.id.account_entry_avatar) - onView(withText("PixelDroid Developer")).check(matches(isDisplayed())) + onView(withText("admin")).check(matches(isDisplayed())) } @Test @@ -123,12 +123,12 @@ class DrawerMenuTest { // Check that profile activity was opened. waitForView(R.id.editButton) onView(withId(R.id.editButton)).check(matches(isDisplayed())) - val followingText = context.resources.getQuantityString(R.plurals.nb_following, 3, 3) + val followingText = context.resources.getQuantityString(R.plurals.nb_following, 2, 2) waitForView(R.id.nbFollowingTextView, allOf(withId(R.id.nbFollowingTextView), withText(followingText))) onView(withText(followingText)).perform(click()) waitForView(R.id.account_entry_avatar) - onView(withText("User 2")).check(matches(isDisplayed())) + onView(withText("ros_testing")).check(matches(isDisplayed())) } /*@Test @@ -152,11 +152,11 @@ class DrawerMenuTest { waitForView(R.id.account_entry_avatar) // Open follower's profile - onView(withText("@pixeldroid")).perform(click()) + onView(withText("@admin")).perform(click()) waitForView(R.id.profilePictureImageView) - onView(withId(R.id.accountNameTextView)).check(matches(withText("PixelDroid Developer"))) + onView(withId(R.id.accountNameTextView)).check(matches(withText("admin"))) } @Test @@ -171,10 +171,10 @@ class DrawerMenuTest { waitForView(R.id.account_entry_avatar) // Open following's profile - onView(withText("@user2")).perform(click()) + onView(withText("@ros_testing")).perform(click()) waitForView(R.id.profilePictureImageView) - onView(withId(R.id.accountNameTextView)).check(matches(withText("User 2"))) + onView(withId(R.id.accountNameTextView)).check(matches(withText("ros_testing"))) } @Test diff --git a/app/src/androidTest/java/org/pixeldroid/app/HomeFeedTest.kt b/app/src/androidTest/java/org/pixeldroid/app/HomeFeedTest.kt index 8d6cefb1..b5f98b44 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/HomeFeedTest.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/HomeFeedTest.kt @@ -21,12 +21,14 @@ import org.hamcrest.core.IsInstanceOf import org.hamcrest.core.StringContains.containsString import org.junit.After import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.model.Statement @RunWith(AndroidJUnit4::class) +@Ignore("Disabled because feed behaviour is erratic and flaky to test") class HomeFeedTest { private lateinit var activityScenario: ActivityScenario @@ -81,12 +83,12 @@ class HomeFeedTest { //Wait for the feed to load waitForView(R.id.albumPager) - onView(withId(R.id.list)).perform(scrollToPosition(4)) + onView(withId(R.id.list)).perform(scrollToPosition(2)) onView(first(IsInstanceOf.instanceOf(TabLayout.TabView::class.java))).perform(ViewActions.click()) - onView(first(withId(R.id.description))).check(matches(withText(containsString("@user2")))); + onView(first(withId(R.id.description))).check(matches(withText(containsString("View from a plane, nice clouds :)")))) } @Test @@ -100,13 +102,13 @@ class HomeFeedTest { scrollToPosition(3) ) - onView(allOf(withText(containsString("randomNoise")))) - .perform(clickClickableSpan("#randomNoise")) + onView(allOf(withText(containsString("randomnoise")))) + .perform(clickClickableSpan("#randomnoise")) - waitForView(R.id.action_bar, allOf(withText("#randomNoise"), not(withId(R.id.description)))) + waitForView(R.id.action_bar, allOf(withText("#randomnoise"), not(withId(R.id.description)))) onView(withId(R.id.action_bar)).check(matches(isDisplayed())); - onView(allOf(withText("#randomNoise"), not(withId(R.id.description)))).check(matches(withParent(withId(R.id.action_bar)))); + onView(allOf(withText("#randomnoise"), not(withId(R.id.description)))).check(matches(withParent(withId(R.id.action_bar)))) } /* @Test diff --git a/app/src/androidTest/java/org/pixeldroid/app/IntentTest.kt b/app/src/androidTest/java/org/pixeldroid/app/IntentTest.kt index 6f312031..ea09a457 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/IntentTest.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/IntentTest.kt @@ -30,6 +30,10 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.Timeout import org.junit.runner.RunWith +import org.pixeldroid.app.posts.PostActivity +import org.pixeldroid.app.utils.api.objects.Attachment +import org.pixeldroid.app.utils.api.objects.Mention +import org.pixeldroid.app.utils.api.objects.Status import java.time.Instant @@ -66,26 +70,40 @@ class IntentTest { @Test fun clickingMentionOpensProfile() { - ActivityScenario.launch(MainActivity::class.java) - - val account = Account("265626292148375552", "user2", "user2", - "https://testing2.pixeldroid.org/user2", "User 2", - "", - "https://testing2.pixeldroid.org/storage/avatars/default.jpg?v=0", - "https://testing2.pixeldroid.org/storage/avatars/default.jpg?v=0", - "", "", false, emptyList(), null, - Instant.parse("2021-02-11T23:44:03.000000Z"), 0, 1, 2, - null, null, false, null) + val accountExpected = Account(id="448137467259420673", username="ros_testing", acct="ros_testing", url="https://testing.pixeldroid.org/ros_testing", display_name="ros_testing", note="", avatar="https://testing.pixeldroid.org/storage/avatars/default.jpg?v=0", avatar_static="https://testing.pixeldroid.org/storage/avatars/default.jpg?v=0", header="https://testing.pixeldroid.org/storage/headers/missing.png", header_static="https://testing.pixeldroid.org/storage/headers/missing.png", locked=false, emojis=emptyList(), discoverable=true, created_at=Instant.parse("2022-06-30T14:58:18Z"), statuses_count=1, followers_count=2, following_count=0, moved=null, fields=emptyList(), bot=false, source=null, suspended=null, mute_expires_at=null) + val accountPoster = Account(id="457218336143343773", username="pixeldroid", acct="pixeldroid", url="https://testing.pixeldroid.org/pixeldroid", display_name="PixelDroid Developer", note="", avatar="https://testing.pixeldroid.org/storage/avatars/default.jpg?v=0", avatar_static="https://testing.pixeldroid.org/storage/avatars/default.jpg?v=0", header="https://testing.pixeldroid.org/storage/headers/missing.png", header_static="https://testing.pixeldroid.org/storage/headers/missing.png", locked=false, emojis=emptyList(), discoverable=true, created_at=Instant.parse("2022-07-25T16:22:26Z"), statuses_count=3, followers_count=1, following_count=2, moved=null, fields=emptyList(), bot=false, source=null, suspended=null, mute_expires_at=null) val expectedIntent: Matcher = CoreMatchers.allOf( - IntentMatchers.hasExtra(ACCOUNT_TAG, account) + IntentMatchers.hasExtra(ACCOUNT_TAG, accountExpected) ) - "2021-02-11T23:44:03.000000Z" + + + val attachment = Attachment(id="31", type=Attachment.AttachmentType.image, url="https://testing.pixeldroid.org/storage/m/_v2/457218336143343773/7a6475c83-a44db4/RcuV81RiDorC/RCtbr01ttKfqATIA9TYL7MOatlYuxdkm3CsNYydB.jpg", preview_url="https://testing.pixeldroid.org/storage/m/_v2/457218336143343773/7a6475c83-a44db4/RcuV81RiDorC/RCtbr01ttKfqATIA9TYL7MOatlYuxdkm3CsNYydB_thumb.jpg", remote_url=null, meta= Attachment.Meta( + focus = Attachment.Meta.Focus(x = 0.0, y = 0.0), + original = Attachment.Meta.Image(width = 720, + height = 576, + size = "720x576", + aspect = 1.25)), description=null, blurhash="U4HoQs014-~UyD4rRit200~mIe034s-*srIZ", text_url=null) + val post = Status( + id = "457277566298267808", + content = "@ros_testing nice I have this too", + account = accountPoster, + media_attachments = listOf(attachment), + created_at = Instant.parse("2022-07-25T20:17:47Z"), + mentions = listOf(Mention(id="448137467259420673", username="ros_testing", acct="ros_testing", url="https://testing.pixeldroid.org/ros_testing")), + uri = "https://testing.pixeldroid.org/p/pixeldroid/457277566298267808", + url = "https://testing.pixeldroid.org/p/pixeldroid/457277566298267808", + ) + + + val intent = Intent(context, PostActivity::class.java) + intent.putExtra(Status.POST_TAG, post) + ActivityScenario.launch(intent) + waitForView(R.id.description) //Click the mention - Espresso.onView(ViewMatchers.withId(R.id.list)) - .perform(RecyclerViewActions.actionOnItemAtPosition - (0, clickClickableSpanInDescription("@user2"))) + Espresso.onView(ViewMatchers.withId(R.id.description)) + .perform(clickClickableSpanInDescription("@ros_testing")) //Wait a bit Thread.sleep(1000) diff --git a/app/src/androidTest/java/org/pixeldroid/app/LoginActivityOfflineTest.kt b/app/src/androidTest/java/org/pixeldroid/app/LoginActivityOfflineTest.kt index 324aa9db..5bdcd016 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/LoginActivityOfflineTest.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/LoginActivityOfflineTest.kt @@ -4,37 +4,50 @@ import android.content.Context import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.IdlingResource import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.junit.* -import org.pixeldroid.app.testUtility.clearData -import org.pixeldroid.app.testUtility.initDB -import org.pixeldroid.app.utils.db.AppDatabase +import com.linkedin.android.testbutler.TestButler +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test import org.junit.rules.Timeout import org.junit.runner.RunWith +import org.pixeldroid.app.testUtility.ConnectivityIdlingResource +import org.pixeldroid.app.testUtility.clearData +import org.pixeldroid.app.testUtility.initDB import org.pixeldroid.app.testUtility.waitForView +import org.pixeldroid.app.utils.db.AppDatabase + @RunWith(AndroidJUnit4::class) -@Ignore("Ignore until we can get TestButler to work on CI") class LoginActivityOfflineTest { private lateinit var context: Context + private lateinit var waitForOffline: IdlingResource private lateinit var db: AppDatabase @get:Rule var globalTimeout: Timeout = Timeout.seconds(100) + + @Before fun before() { - //TestButler.setWifiState(false) - //TestButler.setGsmState(false) - context = ApplicationProvider.getApplicationContext() + TestButler.setWifiState(false) + TestButler.setGsmState(false) + context = ApplicationProvider.getApplicationContext() + waitForOffline = ConnectivityIdlingResource("resourceName", + context, ConnectivityIdlingResource.WAIT_FOR_DISCONNECTION) db = initDB(context) db.clearAllTables() + IdlingRegistry.getInstance().register(waitForOffline) ActivityScenario.launch(LoginActivity::class.java) } @@ -53,9 +66,10 @@ class LoginActivityOfflineTest { @After fun after() { - //TestButler.setWifiState(true) - //TestButler.setGsmState(true) + TestButler.setWifiState(true) + TestButler.setGsmState(true) db.close() clearData() + IdlingRegistry.getInstance().unregister(waitForOffline) } } \ No newline at end of file diff --git a/app/src/androidTest/java/org/pixeldroid/app/LoginActivityOnlineTest.kt b/app/src/androidTest/java/org/pixeldroid/app/LoginActivityOnlineTest.kt index 3bb7dde5..743f0cf8 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/LoginActivityOnlineTest.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/LoginActivityOnlineTest.kt @@ -25,6 +25,7 @@ import org.junit.Test import org.junit.rules.Timeout import org.junit.runner.RunWith import org.pixeldroid.app.testUtility.PACKAGE_ID +import org.pixeldroid.app.testUtility.waitForView @RunWith(AndroidJUnit4::class) class LoginActivityOnlineTest { @@ -73,13 +74,15 @@ class LoginActivityOnlineTest { @Test fun wrongIntentReturnInfoFailsTest() { pref.edit() - .putString("domain", "https://dhbfnhgbdbbet") + .putString("domain", "https://invalid.pixeldroid.org") .putString("clientID", "iwndoiuqwnd") .putString("clientSecret", "wlifowed") .apply() val uri = Uri.parse("oauth2redirect://${PACKAGE_ID}?code=sdfdqsf") val intent = Intent(ACTION_VIEW, uri, context, LoginActivity::class.java) ActivityScenario.launch(intent) + waitForView(R.id.editText) + Thread.sleep(100) onView(withId(R.id.editText)).check(matches( hasErrorText(context.getString(R.string.token_error)) )) diff --git a/app/src/androidTest/java/org/pixeldroid/app/MockedServerTest.kt b/app/src/androidTest/java/org/pixeldroid/app/MockedServerTest.kt index fc6fea22..5631f331 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/MockedServerTest.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/MockedServerTest.kt @@ -10,6 +10,7 @@ import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.* import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.tabs.TabLayout import org.hamcrest.CoreMatchers.anyOf import org.pixeldroid.app.testUtility.* @@ -32,7 +33,6 @@ class MockedServerTest { db = initDB(context) db.clearAllTables() db.instanceDao().insertInstance(testiTestoInstance) - db.userDao().insertUser(testiTesto) db.close() activityScenario = ActivityScenario.launch(MainActivity::class.java) @@ -58,22 +58,22 @@ class MockedServerTest { */ @Test fun searchHashtags() { - activityScenario.onActivity{ - a -> a.findViewById(R.id.tabs).getTabAt(1)?.select() - } + activityScenario.onActivity { + it.findViewById(R.id.tabs).selectedItemId = R.id.page_2 + } - onView(withId(R.id.search)).perform(typeSearchViewText("#randomNoise")) + onView(withId(R.id.search)).perform(typeSearchViewText("#randomnoise")) waitForView(R.id.tag_name) - onView(first(withId(R.id.tag_name))).check(matches(withText("#randomNoise"))) + onView(first(withId(R.id.tag_name))).check(matches(withText("#randomnoise"))) } @Test fun openDiscoverPost(){ - activityScenario.onActivity{ - a -> a.findViewById(R.id.tabs).getTabAt(1)?.select() + activityScenario.onActivity { + it.findViewById(R.id.tabs).selectedItemId = R.id.page_2 } waitForView(R.id.postPreview) @@ -83,25 +83,25 @@ class MockedServerTest { waitForView(R.id.username) onView(withId(R.id.username)).check(matches(anyOf( - withSubstring("User "), + withSubstring("ros_testing"), withSubstring("PixelDroid Developer"), - withSubstring("Testi Testo") + withSubstring("admin") ))) } @Test fun searchAccounts() { - activityScenario.onActivity{ - a -> a.findViewById(R.id.tabs).getTabAt(1)?.select() + activityScenario.onActivity { + it.findViewById(R.id.tabs).selectedItemId = R.id.page_2 } waitForView(R.id.search) - onView(withId(R.id.search)).perform(typeSearchViewText("@user3")) + onView(withId(R.id.search)).perform(typeSearchViewText("@pixeldroid")) waitForView(R.id.account_entry_username) - onView(first(withId(R.id.account_entry_username))).check(matches(withText("User 3"))) + onView(first(withId(R.id.account_entry_username))).check(matches(withText("PixelDroid Developer"))) } @@ -187,44 +187,30 @@ class MockedServerTest { onView(first(withText("Andrea"))).check(matches(withId(R.id.username))) }*/ - @Test - fun swipingRightStopsAtHomepage() { - activityScenario.onActivity { - a -> a.findViewById(R.id.tabs).getTabAt(4)?.select() - } // go to the last tab - - waitForView(R.id.main_activity_main_linear_layout) - - onView(withId(R.id.main_activity_main_linear_layout)) - .perform(ViewActions.swipeRight()) // notifications - .perform(ViewActions.swipeRight()) // camera - .perform(ViewActions.swipeRight()) // search - .perform(ViewActions.swipeRight()) // homepage - .perform(ViewActions.swipeRight()) // should stop at homepage - onView(withId(R.id.list)).check(matches(isDisplayed())) - } @Test fun swipingLeftStopsAtPublicTimeline() { activityScenario.onActivity { - a -> a.findViewById(R.id.tabs).getTabAt(0)?.select() + it.findViewById(R.id.tabs).selectedItemId = R.id.page_1 } - waitForView(R.id.main_activity_main_linear_layout) + waitForView(R.id.view_pager) - onView(withId(R.id.main_activity_main_linear_layout)) + onView(withId(R.id.view_pager)) .perform(ViewActions.swipeLeft()) // notifications .perform(ViewActions.swipeLeft()) // camera .perform(ViewActions.swipeLeft()) // search .perform(ViewActions.swipeLeft()) // homepage .perform(ViewActions.swipeLeft()) // should stop at homepage - onView(withId(R.id.list)).check(matches(isDisplayed())) + activityScenario.onActivity { + assert(it.findViewById(R.id.tabs).selectedItemId == R.id.page_5) + } } @Test fun swipingPublicTimelineWorks() { activityScenario.onActivity { - a -> a.findViewById(R.id.tabs).getTabAt(4)?.select() + it.findViewById(R.id.tabs).selectedItemId = R.id.page_5 } // go to the last tab waitForView(R.id.view_pager) @@ -236,10 +222,8 @@ class MockedServerTest { .perform(ViewActions.swipeRight()) // homepage .perform(ViewActions.swipeRight()) // should stop at homepage - onView(withId(R.id.list)).check(matches(isDisplayed())) - activityScenario.onActivity { - a -> assert(a.findViewById(R.id.tabs).getTabAt(0)?.isSelected ?: false) + assert(it.findViewById(R.id.tabs).selectedItemId == R.id.page_1) } } /* diff --git a/app/src/androidTest/java/org/pixeldroid/app/NotificationWorkerTest.kt b/app/src/androidTest/java/org/pixeldroid/app/NotificationWorkerTest.kt index 25f0c43a..3f6cf2b6 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/NotificationWorkerTest.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/NotificationWorkerTest.kt @@ -36,17 +36,17 @@ class NotificationWorkerTest { private val secondToLatestNotification: Notification = Notification( - id = "1", + id = "78", type = Notification.NotificationType.follow, - created_at = Instant.parse("2021-09-19T19:23:30Z"), + created_at = Instant.parse("2022-07-25T16:23:45Z"), account = Account( id = "344399325768278017", username = "pixeldroid", acct = "pixeldroid", - url = "https://testing.pixeldroid.org/pixeldroid", + url = "${testiTesto.instance_uri}/pixeldroid", display_name = "PixelDroid", note = "", - avatar = "https://testing.pixeldroid.org/storage/avatars/default.jpg?v=0", + avatar = "${testiTesto.instance_uri}/storage/avatars/default.jpg?v=0", avatar_static = null, header = null, header_static = null, @@ -63,8 +63,8 @@ class NotificationWorkerTest { source = null ), status = null, - user_id = "344399082242686977", - instance_uri = "https://testing.pixeldroid.org" + user_id = testiTesto.user_id, + instance_uri = testiTesto.instance_uri ) @Before fun setup() { @@ -92,7 +92,7 @@ class NotificationWorkerTest { @Test fun testNotificationWorker() { val expectedAppName = context.getString(R.string.app_name) - val expectedText = "user1 followed you" + val expectedText = "admin liked your post" // Run the worker synchronously val worker = TestListenableWorkerBuilder(context).build() diff --git a/app/src/androidTest/java/org/pixeldroid/app/PostCreationActivityTest.kt b/app/src/androidTest/java/org/pixeldroid/app/PostCreationActivityTest.kt index 0c11b73d..5f772b78 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/PostCreationActivityTest.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/PostCreationActivityTest.kt @@ -40,7 +40,7 @@ class PostCreationActivityTest { @get:Rule val mRuntimePermissionRule: GrantPermissionRule = - GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE) + GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA) private fun File.writeBitmap(bitmap: Bitmap) { outputStream().use { out -> diff --git a/app/src/androidTest/java/org/pixeldroid/app/PostCreationFragmentTest.kt b/app/src/androidTest/java/org/pixeldroid/app/PostCreationFragmentTest.kt index 71788bbd..6e4fa3fb 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/PostCreationFragmentTest.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/PostCreationFragmentTest.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import androidx.test.core.app.ActivityScenario import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.swipeLeft import androidx.test.espresso.assertion.ViewAssertions.matches @@ -16,6 +17,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule +import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.tabs.TabLayout import org.pixeldroid.app.testUtility.* import org.pixeldroid.app.utils.db.AppDatabase @@ -34,26 +36,37 @@ class PostCreationFragmentTest { var globalTimeout: Timeout = Timeout.seconds(30) @get:Rule var runtimePermissionRule: GrantPermissionRule = - GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) + GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.CAMERA) @get:Rule var intentsTestRule: IntentsTestRule = IntentsTestRule(MainActivity::class.java) + private lateinit var viewPager2IdlingResource: ViewPager2IdlingResource + + @Before - fun setup() { - onView(withId(R.id.drawer_layout)) - .perform(swipeLeft()) - .perform(swipeLeft()) - waitForView(R.id.photo_view_button) + fun setUp() { + ActivityScenario.launch(MainActivity::class.java).onActivity { + it.findViewById(R.id.tabs).selectedItemId = R.id.page_3 + viewPager2IdlingResource = ViewPager2IdlingResource(it.findViewById(R.id.view_pager)) + IdlingRegistry.getInstance().register(viewPager2IdlingResource) + } } + @After + fun tearDown() { + IdlingRegistry.getInstance().unregister(viewPager2IdlingResource) + } + + private val expectedIntent: Matcher = hasAction(Intent.ACTION_CHOOSER) + // image choosing intent @Test fun galleryButtonLaunchesGalleryIntent() { - val expectedIntent: Matcher = hasAction(Intent.ACTION_CHOOSER) - intending(expectedIntent) + waitForView(R.id.photo_view_button) + onView(withId(R.id.photo_view_button)).perform(click()) - Thread.sleep(1000) + Thread.sleep(300) intended(expectedIntent) } } @@ -62,6 +75,10 @@ class PostCreationFragmentTest { class PostFragmentUITests { private lateinit var context: Context + @get:Rule + var runtimePermissionRule: GrantPermissionRule = + GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.CAMERA) + @get:Rule var globalTimeout: Timeout = Timeout.seconds(30) private lateinit var db: AppDatabase @@ -90,7 +107,7 @@ class PostFragmentUITests { @Test fun newPostUiTest() { ActivityScenario.launch(MainActivity::class.java).onActivity { - it.findViewById(R.id.tabs).getTabAt(2)!!.select() + it.findViewById(R.id.tabs).selectedItemId = R.id.page_3 } waitForView(R.id.photo_view_button) diff --git a/app/src/androidTest/java/org/pixeldroid/app/ProfileTest.kt b/app/src/androidTest/java/org/pixeldroid/app/ProfileTest.kt index 6eb60830..853fa96d 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/ProfileTest.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/ProfileTest.kt @@ -38,18 +38,16 @@ class ProfileTest { db.close() val intent = Intent(context, ProfileActivity::class.java) - val account = Account(id="344399325768278017", username="pixeldroid", acct="pixeldroid", url="https://testing.pixeldroid.org/pixeldroid", display_name="PixelDroid Developer", note="", avatar="https://testing.pixeldroid.org/storage/avatars/default.jpg?v=0", avatar_static="https://testing.pixeldroid.org/storage/avatars/default.jpg?v=0", header="", header_static="", locked=false, emojis= emptyList(), discoverable=null, created_at=Instant.parse("2021-09-17T08:39:57Z"), statuses_count=0, followers_count=1, following_count=1, moved=null, fields=null, bot=false, source=null) + val account = Account(id="448138207202832386", username="admin", acct="admin", url="https://testing.pixeldroid.org/admin", display_name="admin", note="", avatar="https://testing.pixeldroid.org/storage/avatars/default.jpg?v=0", avatar_static="https://testing.pixeldroid.org/storage/avatars/default.jpg?v=0", header="https://testing.pixeldroid.org/storage/headers/missing.png", header_static="https://testing.pixeldroid.org/storage/headers/missing.png", locked=false, emojis= emptyList(), discoverable=true, created_at=Instant.parse("2022-06-30T15:01:14Z"), statuses_count=1, followers_count=0, following_count=3, moved=null, fields= emptyList(), bot=false, source=null, suspended=null, mute_expires_at=null) intent.putExtra(Account.ACCOUNT_TAG, account) activityScenario = ActivityScenario.launch(intent) - onView(withId(R.id.profileRefreshLayout)).perform(swipeDown()) - Thread.sleep(2000) + waitForView(R.id.followButton) } @After fun after() { clearData() } - @Test fun clickFollowButton() { if (onView(ViewMatchers.withText("Unfollow")).isDisplayed()) { @@ -60,7 +58,7 @@ class ProfileTest { // Follow follow("Unfollow") - } else { + } else if (onView(ViewMatchers.withText("Follow")).isDisplayed()){ //Currently not following // Follow @@ -68,7 +66,7 @@ class ProfileTest { // Unfollow follow("Follow") - } + } else check(false) } private fun follow(follow_or_unfollow: String){ @@ -87,12 +85,12 @@ class ProfileTest { waitForView(R.id.account_entry_username) // Open follower's profile - onView(ViewMatchers.withText("Testi Testo")).perform((ViewActions.click())) + onView(ViewMatchers.withText("PixelDroid Developer")).perform((ViewActions.click())) waitForView(R.id.editButton) //Check that our own profile opened - onView(withId(R.id.editButton)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + onView(withId(R.id.editButton)).isDisplayed() } } \ No newline at end of file diff --git a/app/src/androidTest/java/org/pixeldroid/app/testUtility/ConnectivityIdlingResource.kt b/app/src/androidTest/java/org/pixeldroid/app/testUtility/ConnectivityIdlingResource.kt new file mode 100644 index 00000000..29168e71 --- /dev/null +++ b/app/src/androidTest/java/org/pixeldroid/app/testUtility/ConnectivityIdlingResource.kt @@ -0,0 +1,57 @@ +package org.pixeldroid.app.testUtility + +import android.content.Context +import androidx.test.espresso.IdlingResource +import kotlin.jvm.Volatile +import android.net.ConnectivityManager +import android.net.NetworkInfo +import android.util.Log +import org.pixeldroid.app.testUtility.ConnectivityIdlingResource + +/** + * Created by Kush Saini + * Later converted and adapted to Kotlin for PixelDroid + * Description: + * This [IdlingResource] halts Espresso Test depending on mode which is passed to the constructor + */ +class ConnectivityIdlingResource +/** + * + * @param resourceName name of the resource (used for logging and idempotency of registration + * @param context context + * @param mode if mode is WAIT_FOR_CONNECTION i.e. value is 1 then the [IdlingResource] + * halts test until internet is available and if mode is WAIT_FOR_DISCONNECTION + * i.e. value is 0 then [IdlingResource] waits for internet to disconnect + */(private val resourceName: String, private val context: Context, private val mode: Int) : + IdlingResource { + @Volatile + private var resourceCallback: IdlingResource.ResourceCallback? = null + override fun getName(): String { + return resourceName + } + + override fun isIdleNow(): Boolean { + val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val activeNetwork: NetworkInfo? = cm.activeNetworkInfo + var isConnected = activeNetwork != null && + activeNetwork.isConnectedOrConnecting + if (mode == WAIT_FOR_DISCONNECTION) isConnected = !isConnected + if (isConnected) { + Log.d(TAG, "Connected now!") + resourceCallback!!.onTransitionToIdle() + } else { + Log.d(TAG, "Not connected!") + } + return isConnected + } + + override fun registerIdleTransitionCallback(resourceCallback: IdlingResource.ResourceCallback) { + this.resourceCallback = resourceCallback + } + + companion object { + var WAIT_FOR_CONNECTION = 1 + var WAIT_FOR_DISCONNECTION = 0 + private const val TAG = "ConnectIdlingResource" + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/org/pixeldroid/app/testUtility/CustomMatchers.kt b/app/src/androidTest/java/org/pixeldroid/app/testUtility/CustomMatchers.kt index 5b336b5f..eacbc64b 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/testUtility/CustomMatchers.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/testUtility/CustomMatchers.kt @@ -6,6 +6,7 @@ import android.view.View import android.widget.EditText import android.widget.TextView import androidx.appcompat.widget.SearchView +import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.* import androidx.test.espresso.NoMatchingViewException @@ -16,6 +17,8 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.* import androidx.test.espresso.util.HumanReadables import androidx.test.espresso.util.TreeIterables +import androidx.viewpager.widget.ViewPager +import androidx.viewpager2.widget.ViewPager2 import org.hamcrest.BaseMatcher import org.hamcrest.CoreMatchers.allOf import org.hamcrest.Description @@ -96,7 +99,7 @@ private fun waitForViewViewAction(viewId: Int, viewMatcher: Matcher): View // Iterate through all views on the screen and see if the view we are looking for is there already for (child in TreeIterables.breadthFirstViewTraversal(rootView)) { // found view with required ID - if (viewMatcher.matches(child)) { + if (viewMatcher.matches(child) && child.isVisible) { return } } @@ -357,4 +360,35 @@ fun typeTextInViewWithId(id: Int, text: String) = object : ViewAction { val v = view.findViewById(id) v.text.append(text) } +} + +class ViewPager2IdlingResource(viewPager: ViewPager2) : IdlingResource { + + companion object { + private const val NAME = "viewPagerIdlingResource" + } + + private var isIdle = true // Default to idle since we can't query the scroll state. + private var resourceCallback: IdlingResource.ResourceCallback? = null + + init { + viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageScrollStateChanged(state: Int) { + // Treat dragging as idle, or Espresso will block itself when swiping + isIdle = (state == ViewPager2.SCROLL_STATE_IDLE || state == ViewPager2.SCROLL_STATE_DRAGGING) + + if (isIdle && resourceCallback != null) { + resourceCallback!!.onTransitionToIdle() + } + } + }) + } + + override fun getName() = NAME + + override fun isIdleNow() = isIdle + + override fun registerIdleTransitionCallback(resourceCallback: IdlingResource.ResourceCallback) { + this.resourceCallback = resourceCallback + } } \ No newline at end of file diff --git a/app/src/androidTest/java/org/pixeldroid/app/testUtility/TestRunner.kt b/app/src/androidTest/java/org/pixeldroid/app/testUtility/TestRunner.kt index a603d2bc..674579ad 100644 --- a/app/src/androidTest/java/org/pixeldroid/app/testUtility/TestRunner.kt +++ b/app/src/androidTest/java/org/pixeldroid/app/testUtility/TestRunner.kt @@ -1,5 +1,5 @@ package org.pixeldroid.app.testUtility -/* + import android.os.Bundle import androidx.test.runner.AndroidJUnitRunner import com.linkedin.android.testbutler.TestButler @@ -17,4 +17,3 @@ class TestRunner: AndroidJUnitRunner() { } } - */ \ No newline at end of file diff --git a/app/src/main/java/org/pixeldroid/app/posts/StatusViewHolder.kt b/app/src/main/java/org/pixeldroid/app/posts/StatusViewHolder.kt index 0ab7ec11..31d44b24 100644 --- a/app/src/main/java/org/pixeldroid/app/posts/StatusViewHolder.kt +++ b/app/src/main/java/org/pixeldroid/app/posts/StatusViewHolder.kt @@ -69,13 +69,13 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold this.itemView.visibility = View.VISIBLE this.status = status - val maxImageRatio: Float = status?.media_attachments?.map { + val maxImageRatio: Float = status?.media_attachments?.maxOfOrNull { if (it.meta?.original?.width == null || it.meta.original.height == null) { 1f } else { it.meta.original.width.toFloat() / it.meta.original.height.toFloat() } - }?.maxOrNull() ?: 1f + } ?: 1f val (displayWidth, displayHeight) = displayDimensionsInPx if (displayWidth / maxImageRatio > displayHeight * 3/4f) { diff --git a/app/src/main/java/org/pixeldroid/app/utils/notificationsWorker/NotificationsWorker.kt b/app/src/main/java/org/pixeldroid/app/utils/notificationsWorker/NotificationsWorker.kt index 4ca4d8e6..ddd3d3a7 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/notificationsWorker/NotificationsWorker.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/notificationsWorker/NotificationsWorker.kt @@ -72,11 +72,14 @@ class NotificationsWorker( ) while (!newNotifications.isNullOrEmpty() - && newNotifications.maxOf { it.created_at ?: Instant.MIN } > previouslyLatestNotification?.created_at ?: Instant.MIN + && newNotifications.maxOf { + it.created_at ?: Instant.MIN + } > (previouslyLatestNotification?.created_at ?: Instant.MIN) ) { // Add to db val filteredNewNotifications: List = newNotifications.filter { - it.created_at ?: Instant.MIN > previouslyLatestNotification?.created_at ?: Instant.MIN + (it.created_at ?: Instant.MIN) > (previouslyLatestNotification?.created_at + ?: Instant.MIN) }.map { it.copy(user_id = user.user_id, instance_uri = user.instance_uri) }.sortedBy { it.created_at } diff --git a/app/src/test/java/org/pixeldroid/app/APIUnitTest.kt b/app/src/test/java/org/pixeldroid/app/APIUnitTest.kt index 887c31ab..b7c7d955 100644 --- a/app/src/test/java/org/pixeldroid/app/APIUnitTest.kt +++ b/app/src/test/java/org/pixeldroid/app/APIUnitTest.kt @@ -120,7 +120,7 @@ fun assertStatusEqualsToReference(actual: Status){ assert( ((actual.id=="140364967936397312" && actual.uri=="https://pixelfed.de/p/Miike/140364967936397312" - && actual.created_at == Instant.parse("2020-03-03T08:00:16.+00:00") + && actual.created_at == Instant.parse("2020-03-03T08:00:16Z") && actual.account!!.id=="115114166443970560"&& actual.account!!.username=="Miike"&& actual.account!!.acct=="Miike" && actual.account!!.url=="https://pixelfed.de/Miike"&& actual.account!!.display_name=="Miike Duart"&& actual.account!!.note==""&& //actual.account!!.avatar=="https://pixelfed.de/storage/avatars/011/511/416/644/397/056/0/ZhaopLJWTWJ3hsVCS5pS_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35"&& diff --git a/app/src/test/java/org/pixeldroid/app/PostUnitTest.kt b/app/src/test/java/org/pixeldroid/app/PostUnitTest.kt index 0978fda4..f05dd974 100644 --- a/app/src/test/java/org/pixeldroid/app/PostUnitTest.kt +++ b/app/src/test/java/org/pixeldroid/app/PostUnitTest.kt @@ -7,7 +7,7 @@ import java.time.Instant class PostUnitTest { private val status = Status(id="140364967936397312", uri="https://pixelfed.de/p/Miike/140364967936397312", - created_at= Instant.parse("2020-03-03T08:00:16+00:00"), + created_at= Instant.parse("2022-07-25T14:57:40.000Z"), account= Account(id="115114166443970560", username="Miike", acct="Miike", url="https://pixelfed.de/Miike", display_name="Miike Duart", note="", avatar="https://pixelfed.de/storage/avatars/011/511/416/644/397/056/0/ZhaopLJWTWJ3hsVCS5pS_avatar.png?v=d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35", diff --git a/build.gradle b/build.gradle index 80d7c336..69b9e357 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.1' + classpath 'com.android.tools.build:gradle:7.3.0-beta05' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8e59e764..029e6fa6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ -#Tue Jun 07 20:42:16 CEST 2022 +#Tue Jul 26 13:21:08 GMT 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME -distributionSha256Sum=b586e04868a22fd817c8971330fec37e298f3242eb85c374181b12d637f80302 +distributionSha256Sum=8cc27038d5dbd815759851ba53e70cf62e481b87494cc97cfd97982ada5ba634