diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/domain/GetSpecializationsListUseCaseImpl.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/domain/GetSpecializationsListUseCaseImpl.kt new file mode 100644 index 00000000..ac3e52f8 --- /dev/null +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/domain/GetSpecializationsListUseCaseImpl.kt @@ -0,0 +1,9 @@ +package ru.yeahub.interview_trainer.impl.createQuiz.domain + +class GetSpecializationsListUseCaseImpl( + private val repository: CreateQuizRepositoryApi, +) : GetSpecializationsUseCase { + override suspend fun invoke( + request: SpecializationsRequest, + ): DomainSpecializationListResponse = repository.getSpecializationsList(request = request) +} \ No newline at end of file diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/domain/GetSpecializationsUseCase.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/domain/GetSpecializationsUseCase.kt new file mode 100644 index 00000000..4171b335 --- /dev/null +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/domain/GetSpecializationsUseCase.kt @@ -0,0 +1,5 @@ +package ru.yeahub.interview_trainer.impl.createQuiz.domain + +interface GetSpecializationsUseCase { + suspend operator fun invoke(request: SpecializationsRequest): DomainSpecializationListResponse +} \ No newline at end of file diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/presentation/CreateQuizScreenMapper.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/presentation/CreateQuizScreenMapper.kt index 47831a56..1abd7353 100644 --- a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/presentation/CreateQuizScreenMapper.kt +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/presentation/CreateQuizScreenMapper.kt @@ -1,13 +1,20 @@ package ru.yeahub.interview_trainer.impl.createQuiz.presentation +import ru.yeahub.interview_trainer.impl.createQuiz.domain.DomainSpecialization + object CreateQuizScreenMapper { fun getScreenState( - specializations: List, + specializations: List, selectedSpecializationId: Long, questionsCount: Int, ): CreateQuizState = CreateQuizState.Loaded( - specializations = specializations, + specializations = specializations.map { domainSpec -> + CreateQuizState.Loaded.VoSpecialization( + id = domainSpec.id, + title = domainSpec.title + ) + }, selectedSpecializationId = selectedSpecializationId, questionsCount = questionsCount ) diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/presentation/CreateQuizViewModel.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/presentation/CreateQuizViewModel.kt index e39eaa93..1a163104 100644 --- a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/presentation/CreateQuizViewModel.kt +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/presentation/CreateQuizViewModel.kt @@ -10,9 +10,11 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import ru.yeahub.core_utils.BaseViewModel -import ru.yeahub.interview_trainer.impl.createQuiz.ui.specializations +import ru.yeahub.interview_trainer.impl.createQuiz.domain.GetSpecializationsUseCase +import ru.yeahub.interview_trainer.impl.createQuiz.domain.SpecializationsRequest open class CreateQuizViewModel( + private val getSpecializationsListUseCase: GetSpecializationsUseCase, private val screenMapper: CreateQuizScreenMapper, ) : BaseViewModel() { @@ -25,8 +27,10 @@ open class CreateQuizViewModel( val screenState = userInputState .map { userInput -> + val request = SpecializationsRequest(page = 1, limit = 99) + screenMapper.getScreenState( - specializations = specializations, + specializations = getSpecializationsListUseCase(request).data, selectedSpecializationId = userInput.selectedSpecializationId, questionsCount = userInput.questionsCount ) @@ -69,32 +73,26 @@ open class CreateQuizViewModel( } private fun incrementQuestionsCount(questionsCount: Int) { - viewModelScopeSafe.launch { - userInputState.update { currentInputState -> - val incrementedCount = questionsCount + 1 - val newCount = incrementedCount.coerceAtMost(MAX_QUESTIONS_COUNT) + userInputState.update { currentInputState -> + val incrementedCount = questionsCount + 1 + val newCount = incrementedCount.coerceAtMost(MAX_QUESTIONS_COUNT) - currentInputState.copy(questionsCount = newCount) - } + currentInputState.copy(questionsCount = newCount) } } private fun decrementQuestionsCount(questionsCount: Int) { - viewModelScopeSafe.launch { - userInputState.update { currentInputState -> - val incrementedCount = questionsCount - 1 - val newCount = incrementedCount.coerceAtLeast(MIN_QUESTIONS_COUNT) + userInputState.update { currentInputState -> + val incrementedCount = questionsCount - 1 + val newCount = incrementedCount.coerceAtLeast(MIN_QUESTIONS_COUNT) - currentInputState.copy(questionsCount = newCount) - } + currentInputState.copy(questionsCount = newCount) } } private fun changeChosenSpecialization(newSpecializationId: Long) { - viewModelScopeSafe.launch { - userInputState.update { currentInputState -> - currentInputState.copy(selectedSpecializationId = newSpecializationId) - } + userInputState.update { currentInputState -> + currentInputState.copy(selectedSpecializationId = newSpecializationId) } } diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/ui/CreateQuizScreen.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/ui/CreateQuizScreen.kt index 917a3cc5..8ef23837 100644 --- a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/ui/CreateQuizScreen.kt +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/createQuiz/ui/CreateQuizScreen.kt @@ -38,6 +38,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow import ru.yeahub.core_ui.component.ErrorScreen import ru.yeahub.core_ui.component.PrimaryButton import ru.yeahub.core_ui.component.SkillButton @@ -47,8 +48,15 @@ import ru.yeahub.core_ui.example.staticPreview.StaticPreview import ru.yeahub.core_ui.theme.LocalAppTypography import ru.yeahub.core_ui.theme.colors import ru.yeahub.core_utils.common.TextOrResource +import ru.yeahub.core_utils.common.observe import ru.yeahub.interview_trainer.impl.R +import ru.yeahub.interview_trainer.impl.createQuiz.domain.DomainSpecialization +import ru.yeahub.interview_trainer.impl.createQuiz.domain.DomainSpecializationListResponse +import ru.yeahub.interview_trainer.impl.createQuiz.domain.GetSpecializationsUseCase +import ru.yeahub.interview_trainer.impl.createQuiz.domain.SpecializationsRequest +import ru.yeahub.interview_trainer.impl.createQuiz.presentation.CreateQuizCommand import ru.yeahub.interview_trainer.impl.createQuiz.presentation.CreateQuizEvent +import ru.yeahub.interview_trainer.impl.createQuiz.presentation.CreateQuizResult import ru.yeahub.interview_trainer.impl.createQuiz.presentation.CreateQuizScreenMapper import ru.yeahub.interview_trainer.impl.createQuiz.presentation.CreateQuizState import ru.yeahub.interview_trainer.impl.createQuiz.presentation.CreateQuizViewModel @@ -117,6 +125,24 @@ private fun ScreenUI( } } +@Composable +fun HandleCommand( + commandFlow: Flow, + onResult: (CreateQuizResult) -> Unit, +) { + commandFlow.observe { command -> + when (command) { + is CreateQuizCommand.NavigateBack -> onResult(CreateQuizResult.NavigateBack) + is CreateQuizCommand.NavigateToInterviewQuizScreen -> onResult( + CreateQuizResult.NavigateToInterviewQuizScreen( + specializationId = command.specializationId, + questionCount = command.questionCount + ) + ) + } + } +} + @Composable private fun BaseCreateQuizScreen( specializations: List, @@ -390,11 +416,27 @@ fun CreateQuizScreenPreview( @Preview(showBackground = true) @Composable fun DynamicPreviewUI() { + val mockDomainList = specializations.map { voSpec -> + DomainSpecialization(id = voSpec.id, title = voSpec.title) + } + + val mockUseCase = object : GetSpecializationsUseCase { + override suspend fun invoke( + request: SpecializationsRequest, + ): DomainSpecializationListResponse { + delay(RESPONSE_DELAY) + return DomainSpecializationListResponse( + total = mockDomainList.size.toLong(), + data = mockDomainList + ) + } + } + val mockViewModel = viewModelCreator { - CreateQuizViewModel(CreateQuizScreenMapper) + CreateQuizViewModel(mockUseCase, CreateQuizScreenMapper) } - val state by mockViewModel.screenState.collectAsState() + val mockState by mockViewModel.screenState.collectAsState() LaunchedEffect(Unit) { delay(RESPONSE_DELAY) @@ -408,13 +450,13 @@ fun DynamicPreviewUI() { mockViewModel.onEvent(CreateQuizEvent.OnMinusQuestionClick(3)) delay(RESPONSE_DELAY) // должно быть снова 2 - mockViewModel.onEvent(CreateQuizEvent.OnSpecializationClick(21)) + mockViewModel.onEvent(CreateQuizEvent.OnSpecializationClick(27)) // С изначально выбранного Frontend Dev должно быть выбрано Android Dev } ProvidePreviewCompositionLocals { ScreenUI( - state = state, + state = mockState, onEvent = mockViewModel::onEvent, headerText = TextOrResource.Resource(R.string.create_quiz_top_bar_header_text) ) diff --git a/feature/interview-trainer/impl/src/test/java/test/CreateQuizMapperTest.kt b/feature/interview-trainer/impl/src/test/java/test/CreateQuizDataToDomainMapperTest.kt similarity index 96% rename from feature/interview-trainer/impl/src/test/java/test/CreateQuizMapperTest.kt rename to feature/interview-trainer/impl/src/test/java/test/CreateQuizDataToDomainMapperTest.kt index 59282f55..c9acfba3 100644 --- a/feature/interview-trainer/impl/src/test/java/test/CreateQuizMapperTest.kt +++ b/feature/interview-trainer/impl/src/test/java/test/CreateQuizDataToDomainMapperTest.kt @@ -10,20 +10,9 @@ import ru.yeahub.network_api.models.GetSpecializationResponse import ru.yeahub.network_api.models.GetSpecializationsResponse import ru.yeahub.test.TestArgumentsProvider -class CreateQuizMapperTest { +class CreateQuizDataToDomainMapperTest { private val toDomainMapper = CreateQuizDataToDomainMapper() - //CreateQuizDataToDomainMapper параметризированный тест - class ArgumentsProvider : - TestArgumentsProvider() { - override fun testCases(): List = listOf( - SpecializationSelectionDataToDomainMapperTestCase( - dataToTest = SpecializationExampleDataClasses.defaultSpecialListResponse, - expectedResult = SpecializationExampleDataClasses.defaultDomainSpecialListResponse - ) - ) - } - @ParameterizedTest @ArgumentsSource(ArgumentsProvider::class) fun specializationSelectionDataToDomainMapperTestCase( @@ -79,4 +68,14 @@ class CreateQuizMapperTest { val dataToTest: GetSpecializationsResponse, val expectedResult: DomainSpecializationListResponse, ) + + class ArgumentsProvider : + TestArgumentsProvider() { + override fun testCases(): List = listOf( + SpecializationSelectionDataToDomainMapperTestCase( + dataToTest = SpecializationExampleDataClasses.defaultSpecialListResponse, + expectedResult = SpecializationExampleDataClasses.defaultDomainSpecialListResponse + ) + ) + } } \ No newline at end of file diff --git a/feature/interview-trainer/impl/src/test/java/test/CreateQuizScreenMapperTest.kt b/feature/interview-trainer/impl/src/test/java/test/CreateQuizScreenMapperTest.kt new file mode 100644 index 00000000..a041a7d3 --- /dev/null +++ b/feature/interview-trainer/impl/src/test/java/test/CreateQuizScreenMapperTest.kt @@ -0,0 +1,89 @@ +package test + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ArgumentsSource +import ru.yeahub.interview_trainer.impl.createQuiz.domain.DomainSpecialization +import ru.yeahub.interview_trainer.impl.createQuiz.presentation.CreateQuizScreenMapper +import ru.yeahub.interview_trainer.impl.createQuiz.presentation.CreateQuizState +import ru.yeahub.test.TestArgumentsProvider + +class CreateQuizScreenMapperTest { + + @ParameterizedTest + @ArgumentsSource(ArgumentsProvider::class) + fun getScreenStateTest( + testCase: CreateQuizScreenMapperTestCase, + ) { + val result = CreateQuizScreenMapper.getScreenState( + specializations = testCase.specializations, + selectedSpecializationId = testCase.selectedSpecializationId, + questionsCount = testCase.questionsCount + ) + Assertions.assertEquals(testCase.expectedResult, result) + } + + object ScreenMapperExampleDataClasses { + val defaultDomainSpecial = DomainSpecialization( + id = 0L, + title = "default title num 0" + ) + + val defaultDomainSpecialWithImage = DomainSpecialization( + id = 1L, + title = "default title num 1" + ) + + val defaultDomainSpecialList = listOf( + defaultDomainSpecial, + defaultDomainSpecialWithImage + ) + + val defaultVoSpecial = CreateQuizState.Loaded.VoSpecialization( + id = 0L, + title = "default title num 0" + ) + + val defaultVoSpecialWithImage = CreateQuizState.Loaded.VoSpecialization( + id = 1L, + title = "default title num 1" + ) + + val defaultLoadedState = CreateQuizState.Loaded( + specializations = listOf(defaultVoSpecial, defaultVoSpecialWithImage), + selectedSpecializationId = 0L, + questionsCount = 10 + ) + + val loadedStateWithDifferentSelection = CreateQuizState.Loaded( + specializations = listOf(defaultVoSpecial, defaultVoSpecialWithImage), + selectedSpecializationId = 1L, + questionsCount = 5 + ) + } + + data class CreateQuizScreenMapperTestCase( + val specializations: List, + val selectedSpecializationId: Long, + val questionsCount: Int, + val expectedResult: CreateQuizState.Loaded, + ) + + class ArgumentsProvider : + TestArgumentsProvider() { + override fun testCases(): List = listOf( + CreateQuizScreenMapperTestCase( + specializations = ScreenMapperExampleDataClasses.defaultDomainSpecialList, + selectedSpecializationId = 0L, + questionsCount = 10, + expectedResult = ScreenMapperExampleDataClasses.defaultLoadedState + ), + CreateQuizScreenMapperTestCase( + specializations = ScreenMapperExampleDataClasses.defaultDomainSpecialList, + selectedSpecializationId = 1L, + questionsCount = 5, + expectedResult = ScreenMapperExampleDataClasses.loadedStateWithDifferentSelection + ) + ) + } +} \ No newline at end of file diff --git a/feature/selection-specializations/impl/src/test/java/test/SpecializationExampleDataClasses.kt b/feature/selection-specializations/impl/src/test/java/test/SpecializationExampleDataClasses.kt deleted file mode 100644 index 38b8d9a8..00000000 --- a/feature/selection-specializations/impl/src/test/java/test/SpecializationExampleDataClasses.kt +++ /dev/null @@ -1,62 +0,0 @@ -package test - -import ru.yeahub.network_api.models.GetSpecializationResponse -import ru.yeahub.network_api.models.GetSpecializationsResponse -import ru.yeahub.selection_specializations.impl.domain.DomainSpecilialization -import ru.yeahub.selection_specializations.impl.domain.DomainSpecilializationListResponse -import ru.yeahub.selection_specializations.impl.presentation.VoSpecilialization - -object SpecializationExampleDataClasses { - val defaultSpecialResponse = GetSpecializationResponse( - id = 0L, - title = "default title num 0", - description = "default description", - imageSrc = null, - createdAt = "01.01.1970", - updatedAt = "01.01.1970" - ) - - val defaultSpecialResponseWithImage = GetSpecializationResponse( - id = 1L, - title = "default title num 1", - description = "default description", - imageSrc = "some image url", - createdAt = "01.01.1970", - updatedAt = "01.01.1970" - ) - - val defaultDomainSpecial = DomainSpecilialization( - id = 0L, - title = "default title num 0" - ) - - val defaultDomainSpecialWithImage = DomainSpecilialization( - id = 1L, - title = "default title num 1" - ) - - val defaultSpecialListResponse = GetSpecializationsResponse( - page = 1L, - limit = 10L, - data = listOf(defaultSpecialResponse, defaultSpecialResponseWithImage), - total = 2L - ) - - val defaultDomainSpecialListResponse = DomainSpecilializationListResponse( - page = 1L, - limit = 10L, - data = listOf(defaultDomainSpecial, defaultDomainSpecialWithImage), - total = 2L - ) - - val defaultVoSpecialization = VoSpecilialization( - id = 0, - title = "default title num 0" - ) - - val defaultVoSpecializationWithImage = VoSpecilialization( - id = 1, - title = "default title num 1" - ) -} - diff --git a/feature/selection-specializations/impl/src/test/java/test/SpecializationSelectionMapperTest.kt b/feature/selection-specializations/impl/src/test/java/test/SpecializationSelectionMapperTest.kt deleted file mode 100644 index 14d957a6..00000000 --- a/feature/selection-specializations/impl/src/test/java/test/SpecializationSelectionMapperTest.kt +++ /dev/null @@ -1,99 +0,0 @@ -package test - -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ArgumentsSource -import ru.yeahub.network_api.models.GetSpecializationResponse -import ru.yeahub.network_api.models.GetSpecializationsResponse -import ru.yeahub.selection_specializations.impl.data.SpecializationSelectionDataToDomainMapper -import ru.yeahub.selection_specializations.impl.domain.DomainSpecilialization -import ru.yeahub.selection_specializations.impl.domain.DomainSpecilializationListResponse -import ru.yeahub.selection_specializations.impl.presentation.SpecializationSelectionScreenMapper.getScreenState -import ru.yeahub.selection_specializations.impl.presentation.VoSpecilialization -import ru.yeahub.test.TestArgumentsProvider -import test.SpecializationExampleDataClasses.defaultDomainSpecial -import test.SpecializationExampleDataClasses.defaultDomainSpecialListResponse -import test.SpecializationExampleDataClasses.defaultDomainSpecialWithImage -import test.SpecializationExampleDataClasses.defaultSpecialListResponse -import test.SpecializationExampleDataClasses.defaultSpecialResponse -import test.SpecializationExampleDataClasses.defaultSpecialResponseWithImage -import test.SpecializationExampleDataClasses.defaultVoSpecialization -import test.SpecializationExampleDataClasses.defaultVoSpecializationWithImage - -class SpecializationSelectionMapperTest { - private val toDomainMapper = SpecializationSelectionDataToDomainMapper() - - class ArgumentsProvider1 : TestArgumentsProvider(){ - override fun testCases(): List = listOf( - SpecializationSelectionDataToDomainMapperTestCase1( - dataToTest = defaultSpecialResponse, - expectedResult = defaultDomainSpecial - ), - SpecializationSelectionDataToDomainMapperTestCase1( - dataToTest = defaultSpecialResponseWithImage, - expectedResult = defaultDomainSpecialWithImage - ) - ) - } - - class ArgumentsProvider2 : TestArgumentsProvider(){ - override fun testCases(): List = listOf( - SpecializationSelectionDataToDomainMapperTestCase2( - dataToTest = defaultSpecialListResponse, - expectedResult = defaultDomainSpecialListResponse - ) - ) - } - - class ArgumentsProvider3 : TestArgumentsProvider(){ - override fun testCases(): List = listOf( - SpecializationSelectionDomainToVoMapperTestCase( - dataToTest = listOf(defaultDomainSpecial, defaultDomainSpecialWithImage), - expectedResult = listOf(defaultVoSpecialization, defaultVoSpecializationWithImage) - ) - ) - } - - @ParameterizedTest - @ArgumentsSource(ArgumentsProvider1::class) - fun specializationSelectionDataToDomainMapperTestCase1( - testCase1: SpecializationSelectionDataToDomainMapperTestCase1 - ){ - val result = toDomainMapper.dataToDomain(testCase1.dataToTest) - assertEquals(testCase1.expectedResult, result) - } - - @ParameterizedTest - @ArgumentsSource(ArgumentsProvider2::class) - fun specializationSelectionDataToDomainMapperTestCase2( - testCase2: SpecializationSelectionDataToDomainMapperTestCase2 - ){ - val result = toDomainMapper.dataListToDomainList(testCase2.dataToTest) - assertEquals(testCase2.expectedResult, result) - } - - @ParameterizedTest - @ArgumentsSource(ArgumentsProvider3::class) - fun specializationSelectionDomainToVoMapperTestCase( - testCase: SpecializationSelectionDomainToVoMapperTestCase - ){ - val result = getScreenState(testCase.dataToTest) - assertEquals(testCase.expectedResult, result) - } -} - -data class SpecializationSelectionDataToDomainMapperTestCase1( - val dataToTest: GetSpecializationResponse, - val expectedResult: DomainSpecilialization -) - -data class SpecializationSelectionDataToDomainMapperTestCase2( - val dataToTest: GetSpecializationsResponse, - val expectedResult: DomainSpecilializationListResponse -) - -data class SpecializationSelectionDomainToVoMapperTestCase( - val dataToTest: List, - val expectedResult: List -) -