diff --git a/app/build.gradle b/app/build.gradle index e93ddb1..98c5b9b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,6 +25,16 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 + } + + testOptions { + unitTests { + includeAndroidResources = true + } + } } dependencies { @@ -39,9 +49,26 @@ dependencies { implementation "io.reactivex.rxjava2:rxandroid:2.1.0" implementation 'com.jakewharton.rxbinding2:rxbinding:2.2.0' implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.2.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'com.google.android.material:material:1.0.0' + + androidTestImplementation 'androidx.test:core-ktx:1.2.0' + androidTestImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion + androidTestImplementation 'androidx.test:runner:' + rootProject.runnerVersion + androidTestImplementation 'androidx.test:rules:' + rootProject.rulesVersion + androidTestImplementation 'androidx.test.espresso:espresso-core:' + rootProject.espressoVersion + androidTestImplementation 'androidx.test.espresso:espresso-intents:' + rootProject.espressoVersion + androidTestImplementation 'androidx.test.espresso:espresso-contrib:' + rootProject.espressoVersion + androidTestImplementation 'androidx.test.ext:truth:' + rootProject.extTruthVersion + androidTestImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion + androidTestImplementation 'org.robolectric:annotations:' + rootProject.robolectricVersion + + testImplementation 'androidx.test:core:' + rootProject.coreVersion + testImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion + testImplementation 'androidx.test.espresso:espresso-core:' + rootProject.espressoVersion + testImplementation 'androidx.test.espresso:espresso-intents:' + rootProject.espressoVersion + testImplementation 'androidx.test.espresso:espresso-contrib:' + rootProject.espressoVersion + testImplementation 'androidx.test.ext:truth:' + rootProject.extTruthVersion + testImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion + testImplementation 'org.robolectric:robolectric:' + rootProject.robolectricVersion } diff --git a/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/AddSocialActivityTest.kt b/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/AddSocialActivityTest.kt new file mode 100644 index 0000000..b8a04ac --- /dev/null +++ b/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/AddSocialActivityTest.kt @@ -0,0 +1,82 @@ +package pl.edu.amu.wmi.socialaggregator + +import androidx.recyclerview.widget.RecyclerView +import androidx.test.core.app.launchActivity +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.contrib.RecyclerViewActions +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import junit.framework.Assert.assertEquals +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import pl.edu.amu.wmi.socialaggregator.TestUtils.actionOnChild +import pl.edu.amu.wmi.socialaggregator.TestUtils.cleanLocalStorage +import pl.edu.amu.wmi.socialaggregator.activity.AddSocialActivity + +@RunWith(AndroidJUnit4::class) +class AddSocialActivityTest { + + @Rule + @JvmField + val activity = IntentsTestRule(AddSocialActivity::class.java) + + + @Test + fun initialState_noAddedSocials_oneAvailableSocial() { + cleanLocalStorage(activity) + + val newActivity = launchActivity() + + assertEquals( + 0, activity.activity.findViewById(R.id.availableSocialsRecyclerView) + .adapter?.itemCount + ) + assertEquals( + 1, activity.activity.findViewById(R.id.addSocialRecyclerView) + .adapter?.itemCount + ) + } + + @Test + fun clickingSocials_movesThemFormAvailableToAdded_etViceVersa() { + cleanLocalStorage(activity) + + onView(withId(R.id.addSocialRecyclerView)) + .perform( + RecyclerViewActions.actionOnItemAtPosition( + 0, + actionOnChild(click(), R.id.socialPlatformImage) + ) + ) + + assertEquals( + 1, activity.activity.findViewById(R.id.availableSocialsRecyclerView) + .adapter?.itemCount + ) + assertEquals( + 0, activity.activity.findViewById(R.id.addSocialRecyclerView) + .adapter?.itemCount + ) + + onView(withId(R.id.availableSocialsRecyclerView)) + .perform( + RecyclerViewActions.actionOnItemAtPosition( + 0, + actionOnChild(click(), R.id.socialPlatformImage) + ) + ) + + assertEquals( + 0, activity.activity.findViewById(R.id.availableSocialsRecyclerView) + .adapter?.itemCount + ) + assertEquals( + 1, activity.activity.findViewById(R.id.addSocialRecyclerView) + .adapter?.itemCount + ) + } + +} \ No newline at end of file diff --git a/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/ExampleInstrumentedTest.kt b/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/ExampleInstrumentedTest.kt index 7ac81ab..b2e11a9 100644 --- a/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/ExampleInstrumentedTest.kt @@ -15,10 +15,12 @@ import org.junit.Assert.* */ @RunWith(AndroidJUnit4::class) class ExampleInstrumentedTest { + @Test fun useAppContext() { // Context of the app under test. val appContext = InstrumentationRegistry.getInstrumentation().targetContext + appContext.filesDir.delete() assertEquals("pl.edu.amu.wmi.socialaggregator", appContext.packageName) } } diff --git a/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/MainActivityTest.kt b/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/MainActivityTest.kt new file mode 100644 index 0000000..6b43ab2 --- /dev/null +++ b/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/MainActivityTest.kt @@ -0,0 +1,38 @@ +package pl.edu.amu.wmi.socialaggregator + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.intent.Intents.intended +import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import pl.edu.amu.wmi.socialaggregator.activity.AddSocialActivity +import pl.edu.amu.wmi.socialaggregator.activity.MainActivity +import pl.edu.amu.wmi.socialaggregator.activity.NewPostActivity + +@RunWith(AndroidJUnit4::class) +class MainActivityTest { + + @Rule + @JvmField + val activity = IntentsTestRule(MainActivity::class.java) + + @Test + fun clickAddNewSocial_shouldOpenAddNewSocialActivity() { + onView(withId(R.id.connectedSocialsButton)).perform(click()) + + intended(hasComponent(AddSocialActivity::class.java.name)) + } + + @Test + fun clickCreatePost_shouldOpenAddNewNewPostActivity() { + onView(withId(R.id.createPostButton)).perform(click()) + + intended(hasComponent(NewPostActivity::class.java.name)) + } + +} \ No newline at end of file diff --git a/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/NewPostActivityTest.kt b/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/NewPostActivityTest.kt new file mode 100644 index 0000000..5d7f8ef --- /dev/null +++ b/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/NewPostActivityTest.kt @@ -0,0 +1,73 @@ +package pl.edu.amu.wmi.socialaggregator + +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.Espresso.* +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.RecyclerViewActions +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import pl.edu.amu.wmi.socialaggregator.TestUtils.atPosition +import pl.edu.amu.wmi.socialaggregator.TestUtils.cleanLocalStorage +import pl.edu.amu.wmi.socialaggregator.TestUtils.getCurrentActivity +import pl.edu.amu.wmi.socialaggregator.activity.MainActivity + +@RunWith(AndroidJUnit4::class) +class NewPostActivityTest { + + @Rule + @JvmField + val activity = IntentsTestRule(MainActivity::class.java) + + @Test + fun clickPublishPost_shouldEndActivity() { + onView(withId(R.id.createPostButton)).perform(click()) + + closeSoftKeyboard() + + onView(withId(R.id.publishPost)).perform(click()) + + assertTrue(getCurrentActivity()!!::class.java.simpleName == "MainActivity") + } + + @Test + fun clickPublishPost_shouldIncreasePostCount() { + cleanLocalStorage(activity) + + onView(withId(R.id.connectedSocialsButton)).perform(click()) + + onView(withId(R.id.addSocialRecyclerView)) + .perform( + RecyclerViewActions.actionOnItemAtPosition( + 0, + TestUtils.actionOnChild(click(), R.id.socialPlatformImage) + ) + ) + + pressBack() + + onView(withId(R.id.createPostButton)).perform(click()) + + closeSoftKeyboard() + + onView(withId(R.id.availableSocials)) + .perform( + RecyclerViewActions.actionOnItemAtPosition( + 0, + TestUtils.actionOnChild(click(), R.id.chip) + ) + ) + + onView(withId(R.id.publishPost)).perform(click()) + + onView(withId(R.id.previousPostsRecyclerView)).check( + matches(atPosition(0, hasDescendant(withText("Facebook")))) + ) + } + +} \ No newline at end of file diff --git a/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/TestUtils.kt b/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/TestUtils.kt new file mode 100644 index 0000000..f87fb36 --- /dev/null +++ b/app/src/androidTest/java/pl/edu/amu/wmi/socialaggregator/TestUtils.kt @@ -0,0 +1,109 @@ +package pl.edu.amu.wmi.socialaggregator + +import android.app.Activity +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.NoMatchingViewException +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.ViewAssertion +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.BoundedMatcher +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry +import androidx.test.runner.lifecycle.Stage +import org.hamcrest.CoreMatchers +import org.hamcrest.Description +import org.hamcrest.Matcher +import org.hamcrest.core.AllOf +import org.junit.runner.RunWith +import java.io.File + +object TestUtils { + + fun cleanLocalStorage(activity: IntentsTestRule) { + deleteRecursive(activity.activity.filesDir) + } + + private fun deleteRecursive(fileOrDirectory: File) { + if (fileOrDirectory.isDirectory()) + for (child in fileOrDirectory.listFiles()) + deleteRecursive(child) + + fileOrDirectory.delete() + } + + fun actionOnChild(action: ViewAction, childId: Int): ViewAction? { + return object : ViewAction { + override fun getDescription(): String { + return "Action on child" + } + + override fun getConstraints(): Matcher { + return AllOf.allOf( + ViewMatchers.isDisplayed(), + ViewMatchers.isAssignableFrom(View::class.java) + ) + } + + override fun perform(uiController: UiController?, view: View?) { + view?.let { + val child = it.findViewById(childId) + action.perform(uiController, child) + } + } + + } + } + + fun atPosition(position: Int, itemMatcher: Matcher): Matcher { + checkNotNull(itemMatcher) + return object : BoundedMatcher(RecyclerView::class.java) { + override fun describeTo(description: Description) { + description.appendText("has item at position $position: ") + itemMatcher.describeTo(description) + } + + override fun matchesSafely(view: RecyclerView): Boolean { + val viewHolder = view.findViewHolderForAdapterPosition(position) + ?: // has no item on such position + return false + return itemMatcher.matches(viewHolder.itemView) + } + } + } + + fun atPosition(position: Int, id: Int, itemMatcher: Matcher): Matcher { + checkNotNull(itemMatcher) + return object : BoundedMatcher(RecyclerView::class.java) { + override fun describeTo(description: Description) { + description.appendText("has item at position $position: ") + itemMatcher.describeTo(description) + } + + override fun matchesSafely(view: RecyclerView): Boolean { + val viewHolder = view.findViewHolderForAdapterPosition(position) + ?: // has no item on such position + return false + val view = viewHolder.itemView.findViewById(id) + return itemMatcher.matches(view) + } + } + } + + fun getCurrentActivity(): Activity? { + var currentActivity: Activity? = null + getInstrumentation().runOnMainSync { + run { + currentActivity = + ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage( + Stage.RESUMED + ).elementAtOrNull(0) + } + } + return currentActivity + } + +} \ No newline at end of file diff --git a/app/src/main/java/pl/edu/amu/wmi/socialaggregator/socialplatforms/FacebookMock.kt b/app/src/main/java/pl/edu/amu/wmi/socialaggregator/socialplatforms/FacebookMock.kt index 2e3f651..496b33b 100644 --- a/app/src/main/java/pl/edu/amu/wmi/socialaggregator/socialplatforms/FacebookMock.kt +++ b/app/src/main/java/pl/edu/amu/wmi/socialaggregator/socialplatforms/FacebookMock.kt @@ -17,6 +17,7 @@ class FacebookMock : SocialPlatform { override fun login(context: Context) { val loginsDir = InternalStorage.getFileOrDir(context, "logins") + loginsDir?.mkdir() if (loginsDir != null) { val loginFile = File(loginsDir, "facebook") loginFile.createNewFile() diff --git a/build.gradle b/build.gradle index aa15fe5..e882ab2 100644 --- a/build.gradle +++ b/build.gradle @@ -26,3 +26,18 @@ allprojects { task clean(type: Delete) { delete rootProject.buildDir } + + + +ext { + buildToolsVersion = "28.0.3" + androidxLibVersion = "1.0.0" + coreVersion = "1.3.0-alpha03" + extJUnitVersion = "1.1.2-alpha03" + runnerVersion = "1.3.0-alpha03" + rulesVersion = "1.3.0-alpha03" + espressoVersion = "3.3.0-alpha03" + extJUnitVersion = "1.1.2-alpha03" + extTruthVersion = "1.3.0-alpha03" + robolectricVersion = "4.3.1" +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 5efc49f..be0da71 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,3 +19,4 @@ android.useAndroidX=true android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official +android.enableUnitTestBinaryResources=true