Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Task 6 completed #6

Merged
merged 15 commits into from
Jul 29, 2024
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[![en](https://img.shields.io/badge/lang-en-blue.svg)](README.md)
[![ru](https://img.shields.io/badge/lang-ru-red.svg)](README.ru.md)
[![wakatime](https://wakatime.com/badge/user/1d230f86-133e-401a-ace9-7805218f18d8/project/65aeb806-8e59-4e25-b954-7ae03f15a76e.svg)](https://wakatime.com/badge/user/1d230f86-133e-401a-ace9-7805218f18d8/project/65aeb806-8e59-4e25-b954-7ae03f15a76e)

ToDo list application developed at [Yandex summer school](https://yandex.ru/yaintern/schools/mobile).

Expand Down
1 change: 1 addition & 0 deletions README.ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[![en](https://img.shields.io/badge/lang-en-blue.svg)](README.md)
[![ru](https://img.shields.io/badge/lang-ru-red.svg)](README.ru.md)
[![wakatime](https://wakatime.com/badge/user/1d230f86-133e-401a-ace9-7805218f18d8/project/65aeb806-8e59-4e25-b954-7ae03f15a76e.svg)](https://wakatime.com/badge/user/1d230f86-133e-401a-ace9-7805218f18d8/project/65aeb806-8e59-4e25-b954-7ae03f15a76e)

Мобильное приложение со списком дел, написанное в рамках [летней школы Яндекса](https://yandex.ru/yaintern/schools/mobile).

Expand Down
93 changes: 93 additions & 0 deletions app/src/androidTest/java/ru/gribbirg/todoapp/AppUiTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package ru.gribbirg.todoapp

import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onChildAt
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import ru.gribbirg.edit.testing.EditFeatureTestingTags
import ru.gribbirg.list.testing.ListFeatureTestingTags
import ru.gribbirg.todoapp.app.TestApplication
import ru.gribbirg.todoapp.di.TestAppComponent

@RunWith(AndroidJUnit4::class)
class AppUiTest {

private val dispatcher = Dispatchers.IO

@get:Rule
val composeTestRule = createComposeRule()

private lateinit var appComponent: TestAppComponent

@Before
fun setContent() {
val context = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
appComponent = (context as TestApplication).testAppComponent
runBlocking {
appComponent.loginRepo.registerUser("1")
}
composeTestRule.setContent {
TodoComposeApp(appComponent)
}
}

@OptIn(ExperimentalTestApi::class)
@Test
fun addItem() {
CoroutineScope(dispatcher).launch {
delay(1000)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

задержка избыточна, тк следующей строкой дожидаешься окончания всех корутин. ниже также

composeTestRule.awaitIdle()
composeTestRule.waitUntilAtLeastOneExists(
hasTestTag(
ListFeatureTestingTags.getItemRowId(
"1"
)
)
)

composeTestRule.onNodeWithTag(ListFeatureTestingTags.ADD_BUTTON).performClick()
composeTestRule.onNodeWithTag(EditFeatureTestingTags.TEXT_FIELD)
.performTextInput("Test task")
composeTestRule.onNodeWithTag(EditFeatureTestingTags.SAVE_BUTTON).performClick()

composeTestRule.onNodeWithText("Test task").assertExists()
}
}

@OptIn(ExperimentalTestApi::class)
@Test
fun deleteItem() {
CoroutineScope(dispatcher).launch {
delay(1000)
composeTestRule.awaitIdle()
composeTestRule.waitUntilAtLeastOneExists(
hasTestTag(
ListFeatureTestingTags.getItemRowId(
"1"
)
)
)

composeTestRule.onNodeWithTag(ListFeatureTestingTags.getItemRowId("1")).onChildAt(2)
.performClick()
composeTestRule.onNodeWithTag(EditFeatureTestingTags.DELETE_BUTTON).performClick()

composeTestRule.onNodeWithTag("1").assertDoesNotExist()
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.gribbirg.todoapp.app

import android.app.Application
import ru.gribbirg.todoapp.di.DaggerTestAppComponent
import ru.gribbirg.todoapp.di.TestAppComponent

class TestApplication : Application() {
internal val testAppComponent: TestAppComponent by lazy {
DaggerTestAppComponent
.factory()
.create(applicationContext)
}
}
15 changes: 15 additions & 0 deletions app/src/androidTest/java/ru/gribbirg/todoapp/app/TestRunner.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ru.gribbirg.todoapp.app

import android.app.Application
import android.content.Context
import androidx.test.runner.AndroidJUnitRunner

class TestRunner : AndroidJUnitRunner() {
override fun newApplication(
cl: ClassLoader?,
className: String?,
context: Context?
): Application {
return super.newApplication(cl, TestApplication::class.java.name, context)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ru.gribbirg.todoapp.di

import android.content.Context
import dagger.BindsInstance
import dagger.Component
import ru.gribbirg.data.di.DataScope
import ru.gribbirg.domain.repositories.LoginRepository
import ru.gribbirg.network.di.modules.ApiClientScope
import ru.gribbirg.todoapp.di.modules.AboutFeatureModule
import ru.gribbirg.todoapp.di.modules.DataModule
import ru.gribbirg.todoapp.di.modules.EditFeatureModule
import ru.gribbirg.todoapp.di.modules.ListFeatureModule
import ru.gribbirg.todoapp.di.modules.TestAppModule
import ru.gribbirg.todoapp.di.modules.TestDataModule
import ru.gribbirg.todoapp.di.modules.TestNetworkModule
import ru.gribbirg.todoapp.di.modules.UtilsModule
import ru.gribbirg.utils.di.modules.AppSettingsScope


@Component(
modules = [
DataModule::class,
ListFeatureModule::class,
EditFeatureModule::class,
UtilsModule::class,
AboutFeatureModule::class,
TestNetworkModule::class,
TestAppModule::class,
TestDataModule::class,
]
)
@DataScope
@AppScope
@ApiClientScope
@AppSettingsScope
internal interface TestAppComponent : AppComponent {
@Component.Factory
interface Factory {
fun create(
@BindsInstance context: Context
): TestAppComponent
}

val loginRepo: LoginRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ru.gribbirg.todoapp.di.modules

import dagger.Binds
import dagger.Module
import ru.gribbirg.todoapp.di.AppComponent
import ru.gribbirg.todoapp.di.TestAppComponent

@Module
internal interface TestAppModule {
@Binds
fun appComponent(testAppComponent: TestAppComponent): AppComponent
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ru.gribbirg.todoapp.di.modules

import dagger.Module
import dagger.Provides
import ru.gribbirg.data.di.DataFactory
import ru.gribbirg.data.di.DataScope
import ru.gribbirg.domain.repositories.LoginRepository

@Module
interface TestDataModule {
companion object {
@Provides
@DataScope
fun loginRepo(factory: DataFactory): LoginRepository = factory.createLoginRepository()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.gribbirg.todoapp.di.modules

import dagger.Module
import dagger.Provides
import io.ktor.client.engine.HttpClientEngine
import ru.gribbirg.todoapp.network.mockEngine

@Module
internal interface TestNetworkModule {
companion object {
@Provides
fun httpClientEngine(): HttpClientEngine = mockEngine
}
}
100 changes: 100 additions & 0 deletions app/src/androidTest/java/ru/gribbirg/todoapp/network/MockHttpEngine.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package ru.gribbirg.todoapp.network

import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.engine.mock.respond
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.http.headers
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import ru.gribbirg.domain.model.todo.TodoImportance
import ru.gribbirg.domain.model.todo.TodoItem
import ru.gribbirg.network.dto.TodoItemResponseDto
import ru.gribbirg.network.dto.TodoListResponseDto
import ru.gribbirg.network.dto.toNetworkDto
import java.time.LocalDate
import java.time.LocalDateTime

internal val mockEngine =
MockEngine { request ->
when (request.url.pathSegments.last()) {
"list" -> when (request.method) {
HttpMethod.Get, HttpMethod.Patch -> respond(
content = Json.encodeToString(
TodoListResponseDto(
status = "ok",
revision = 1,
list = items
)
),
status = HttpStatusCode.OK,
headers = headers,
)

HttpMethod.Post -> respond(
content = Json.encodeToString(
TodoItemResponseDto(
status = "ok",
revision = 2,
element = extraItem,
)
),
status = HttpStatusCode.OK,
headers = headers
)

HttpMethod.Put -> respond(
content = Json.encodeToString(
TodoItemResponseDto(
status = "ok",
revision = 2,
element = firstEdited,
)
),
status = HttpStatusCode.OK,
headers = headers
)

else -> respond(
content = "",
status = HttpStatusCode.NotFound,
headers = headers
)
}

else -> respond(
content = "",
status = HttpStatusCode.NotFound,
headers = headers
)
}
}


private val headers = headers {
append(
HttpHeaders.ContentType,
"application/json; charset=utf-8"
)
}

val items = List(5) {
TodoItem(
id = it.toString(),
text = "Дело $it ".repeat(it * 5 + 1),
importance = TodoImportance.entries[it % 3],
deadline = if (it % 3 == 0)
null
else LocalDate.now().plusDays((-100L..100L).random()),
completed = it % 4 == 0,
creationDate = LocalDateTime.now().minusDays((-0L..100L).random()),
editDate = LocalDateTime.now().minusDays((-0L..100L).random()),
).toNetworkDto("1")
}

val extraItem = TodoItem(
id = "-1",
).toNetworkDto("1")

val firstEdited = items.first().copy(text = "New text")
Loading
Loading