diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 82e5439..5836b18 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,6 +5,11 @@ plugins { kotlinAnnotationProcessor id("com.google.dagger.hilt.android").version("2.51.1") } +configurations.all { + resolutionStrategy { + force("org.hamcrest:hamcrest-junit:2.0.0.0") + } +} val packageName = "ru.myitschool.work" @@ -29,18 +34,35 @@ android { targetCompatibility = Version.Kotlin.javaSource } + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } + kotlinOptions { jvmTarget = Version.Kotlin.jvmTarget } } dependencies { - implementation("org.testng:testng:6.9.6") + + val fragmentVersion = "1.8.6" + debugImplementation("androidx.fragment:fragment-testing-manifest:$fragmentVersion") + androidTestImplementation("androidx.fragment:fragment-testing:$fragmentVersion") + + androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") + androidTestImplementation("androidx.test:runner:1.6.2") + androidTestImplementation("androidx.test:rules:1.6.1") + + testImplementation("androidx.test:core:1.6.1") + + testImplementation("junit:junit:4.13.2") + testImplementation("org.robolectric:robolectric:4.14") + defaultLibrary() - val version = "2.24.0" - testImplementation("org.mockito:mockito-core:${version}") - androidTestImplementation("org.mockito:mockito-android:${version}") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0") implementation("androidx.paging:paging-runtime:3.3.6") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01") diff --git a/app/src/androidTest/kotlin/LoginFragmentTest.kt b/app/src/androidTest/kotlin/LoginFragmentTest.kt new file mode 100644 index 0000000..2758ab7 --- /dev/null +++ b/app/src/androidTest/kotlin/LoginFragmentTest.kt @@ -0,0 +1,26 @@ +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.swipeDown +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.filters.LargeTest +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import ru.myitschool.work.R +import ru.myitschool.work.ui.login.LoginFragment +import utils.SwipeRefreshLayoutMatchers.isRefreshing + +@RunWith(AndroidJUnit4ClassRunner::class) +@LargeTest +class LoginFragmentTest { + @get:Rule + val fragmentRule = launchFragmentInContainer() + + @Test + fun onSwipeDataRefreshes() { + onView(withId(R.id.refresh)).perform(swipeDown()) + onView(withId(R.id.refresh)).check(matches(isRefreshing())) + } +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/utils/SwipeRefreshLayoutMatchers.kt b/app/src/androidTest/kotlin/utils/SwipeRefreshLayoutMatchers.kt new file mode 100644 index 0000000..7a8cb5a --- /dev/null +++ b/app/src/androidTest/kotlin/utils/SwipeRefreshLayoutMatchers.kt @@ -0,0 +1,25 @@ +package utils + +import android.view.View +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import androidx.test.espresso.matcher.BoundedMatcher +import org.hamcrest.Description +import org.hamcrest.Matcher + +object SwipeRefreshLayoutMatchers { + + @JvmStatic + fun isRefreshing(): Matcher { + return object : BoundedMatcher( + SwipeRefreshLayout::class.java) { + + override fun describeTo(description: Description) { + description.appendText("is refreshing") + } + + override fun matchesSafely(view: SwipeRefreshLayout): Boolean { + return view.isRefreshing + } + } + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/AdminRepositoryImplTest.kt b/app/src/test/kotlin/AdminRepositoryImplTest.kt new file mode 100644 index 0000000..2ec5dc3 --- /dev/null +++ b/app/src/test/kotlin/AdminRepositoryImplTest.kt @@ -0,0 +1,49 @@ +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import ru.myitschool.work.core.Constants +import ru.myitschool.work.data.AdminRepositoryImpl +import ru.myitschool.work.data.local.CredentialsLocalDataSource +import ru.myitschool.work.data.network.AdminNetworkDataSource +import ru.myitschool.work.domain.admin.AdminRepository + +@RunWith(RobolectricTestRunner::class) +class AdminRepositoryImplTest { + + private lateinit var context: Context + private lateinit var repository: AdminRepository + + @Before + fun setUp() { + context = ApplicationProvider.getApplicationContext() + CredentialsLocalDataSource.buildSource( + context.getSharedPreferences( + Constants.TOKEN_KEY, + Context.MODE_PRIVATE + ) + ) + repository = AdminRepositoryImpl( + networkDataSource = AdminNetworkDataSource, + localCredentialsLocalDataSource = CredentialsLocalDataSource.getInstance() + ) + + } + + @Test + fun `When user have admin permissions and blocked user exist process` () = runTest { + CredentialsLocalDataSource.getInstance().updateToken("pivanov", "admin") + assertNull(repository.blockUser("").exceptionOrNull()) + } + + @Test + fun `When user don not have admin permissions error` () = runTest { + CredentialsLocalDataSource.getInstance().updateToken("", "") + assertNotNull(repository.blockUser("").exceptionOrNull()) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/PassesRepositoryImplTest.kt b/app/src/test/kotlin/PassesRepositoryImplTest.kt new file mode 100644 index 0000000..73b4826 --- /dev/null +++ b/app/src/test/kotlin/PassesRepositoryImplTest.kt @@ -0,0 +1,68 @@ +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import ru.myitschool.work.core.Constants +import ru.myitschool.work.data.PassRepositoryImpl +import ru.myitschool.work.data.local.CredentialsLocalDataSource +import ru.myitschool.work.data.network.PassNetworkDataSource +import ru.myitschool.work.domain.entities.PassEntity + +@RunWith(RobolectricTestRunner::class) +class PassesRepositoryImplTest { + + private lateinit var repository: PassRepositoryImpl + private lateinit var context: Context + + @Before + fun setUp() { + context = ApplicationProvider.getApplicationContext() + CredentialsLocalDataSource.buildSource( + context.getSharedPreferences( + Constants.TOKEN_KEY, + Context.MODE_PRIVATE + ) + ) + repository = PassRepositoryImpl( + networkDataSource = PassNetworkDataSource, + credentialsLocalDataSource = CredentialsLocalDataSource.getInstance() + ) + } + + @Test + fun `When user authorized as user gets only its passes`() = runTest { + CredentialsLocalDataSource.getInstance().updateToken("", "") + assertEquals( + listOf( + PassEntity("", "", ""), + PassEntity("", "", ""), + PassEntity("", "", "") + ), + repository.getCurrentPasses(1, 3).getOrNull() + ) + } + + @Test + fun `When user authorized as admin gets any user's passes`() = runTest { + CredentialsLocalDataSource.getInstance().updateToken("pivanov", "admin") + assertEquals( + listOf( + PassEntity("", "", ""), + PassEntity("", "", ""), + PassEntity("", "", "") + ), + repository.getUsersPasses(1, 3, "").getOrNull() + ) + } + + @Test + fun `When user tries to access admin method error` () = runTest { + CredentialsLocalDataSource.getInstance().updateToken("", "") + assertNull(repository.getUsersPasses(1, 3, "")) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/QrRepositoryImplTest.kt b/app/src/test/kotlin/QrRepositoryImplTest.kt new file mode 100644 index 0000000..33ad0ce --- /dev/null +++ b/app/src/test/kotlin/QrRepositoryImplTest.kt @@ -0,0 +1,55 @@ +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import ru.myitschool.work.core.Constants +import ru.myitschool.work.data.network.QrNetworkDataSource +import ru.myitschool.work.data.QrRepositoryImpl +import ru.myitschool.work.data.local.CredentialsLocalDataSource +import ru.myitschool.work.domain.entities.QrEntity + +@RunWith(RobolectricTestRunner::class) +class QrRepositoryImplTest { + + private lateinit var context: Context + private lateinit var repository: QrRepositoryImpl + + @Before + fun setUp() { + context = ApplicationProvider.getApplicationContext() + CredentialsLocalDataSource.buildSource( + context.getSharedPreferences( + Constants.SERVER_ADDRESS, + Context.MODE_PRIVATE + ) + ) + repository = QrRepositoryImpl( + networkDataSource = QrNetworkDataSource, + credentialsLocalDataSource = CredentialsLocalDataSource.getInstance() + ) + } + + @Test + fun `When QR valid and user logged process`() = runTest { + CredentialsLocalDataSource.getInstance().updateToken("", "") + assertNull(repository.pushQr(QrEntity("")).exceptionOrNull()) + } + + @Test + fun `When QR invalid and user logged error`() = runTest { + CredentialsLocalDataSource.getInstance().updateToken("", "") + assertNotNull(repository.pushQr(QrEntity("*")).exceptionOrNull()) + } + + @Test + fun `When QR valid and user not logged error`() = runTest { + runCatching { + repository.pushQr(QrEntity("*")).exceptionOrNull() + }.let { assert(it.isFailure) } + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/UserRepositoryImplTest.kt b/app/src/test/kotlin/UserRepositoryImplTest.kt new file mode 100644 index 0000000..57bbd7c --- /dev/null +++ b/app/src/test/kotlin/UserRepositoryImplTest.kt @@ -0,0 +1,130 @@ +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import ru.myitschool.work.core.Constants +import ru.myitschool.work.data.UserRepositoryImpl +import ru.myitschool.work.data.local.CredentialsLocalDataSource +import ru.myitschool.work.data.local.UserLocalDataSource +import ru.myitschool.work.data.network.UserNetworkDataSource +import ru.myitschool.work.domain.entities.UserEntity + +@RunWith(RobolectricTestRunner::class) +class UserRepositoryImplTest { + + private lateinit var context: Context + private lateinit var repository: UserRepositoryImpl + + @Before + fun setUp() { + context = ApplicationProvider.getApplicationContext() + CredentialsLocalDataSource.buildSource( + context.getSharedPreferences( + Constants.TOKEN_KEY, + Context.MODE_PRIVATE + ) + ) + repository = UserRepositoryImpl( + credentialsLocalDataSource = CredentialsLocalDataSource.getInstance(), + userLocalDataSource = UserLocalDataSource, + networkDataSource = UserNetworkDataSource + ) + } + + @Test + fun `When user exist return true`() = runTest { + assertEquals(true, repository.isUserExist("pivanov").getOrNull()) + } + + @Test + fun `When user does not exist return false`() = runTest { + assertEquals(false, repository.isUserExist("pivanov").getOrNull()) + } + + @Test + fun `When user existing and password matches success login`() = runTest { + assertEquals( + UserEntity( + isAdmin = true, + name = "", + lastVisit = "", + photoUrl = "", + position = "", + isCardBlocked = false + ), repository.login("pivanov", "admin").getOrNull() + ) + } + + @Test + fun `When user enter incorrect login`() = runTest { + assertNull(repository.login("***", "admin").getOrNull()) + } + + @Test + fun `When user enter incorrect password`() = runTest { + assertNull(repository.login("pivanov", "***").getOrNull()) + } + + @Test + fun `When user enter incorrect login and password`() = runTest { + assertNull(repository.login("***", "***").getOrNull()) + } + + @Test + fun `When user logouts local credentials wipes`() = runTest { + repository.logout() + runCatching { + CredentialsLocalDataSource.getInstance().getToken() + }.let { assert(it.isFailure) } + } + + @Test + fun `Get user by login returns correct user`() = runTest { + CredentialsLocalDataSource.getInstance().updateToken("pivanov", "admin") + assertEquals( + UserEntity( + isAdmin = false, + name = "", + lastVisit = "", + photoUrl = "", + position = "", + isCardBlocked = false + ), + repository.getUserByLogin("").getOrNull() + ) + } + + @Test + fun `Get user by login works with admin roots`() = runTest { + CredentialsLocalDataSource.getInstance().updateToken("pivanov", "admin") + assertNotNull(repository.getUserByLogin("").getOrNull()) + } + + @Test + fun `Get user by login don not works without admin roots`() = runTest { + CredentialsLocalDataSource.getInstance().updateToken("", "") + assertNull(repository.getUserByLogin("").getOrNull()) + } + + @Test + fun `When user successfully logged for the first time it will authorize automatically`() = + runTest { + val loggedUser = repository.login("pivanov", "admin").getOrNull() + assertEquals( + loggedUser, + repository.authorize(CredentialsLocalDataSource.getInstance().getToken()) + ) + } + + @Test + fun `When user successfully logged its info saved locally for current session`() = runTest { + val loggedUser = repository.login("pivanov", "admin").getOrNull() + assertEquals(loggedUser, repository.getCurrentUser()) + } +} \ No newline at end of file