1+ package com.grishko188.pointofinterest.features.categories.ui
2+
3+ import androidx.compose.runtime.remember
4+ import androidx.compose.ui.ExperimentalComposeUiApi
5+ import androidx.compose.ui.focus.FocusRequester
6+ import androidx.compose.ui.graphics.Color
7+ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
8+ import androidx.compose.ui.semantics.Role
9+ import androidx.compose.ui.semantics.SemanticsProperties
10+ import androidx.compose.ui.test.*
11+ import androidx.compose.ui.test.junit4.createComposeRule
12+ import androidx.compose.ui.text.input.TextFieldValue
13+ import com.grishko188.pointofinterest.MockitoHelper.anyNonNull
14+ import com.grishko188.pointofinterest.MockitoHelper.whenever
15+ import com.grishko188.pointofinterest.features.categories.ui.models.CategoryUiModel
16+ import com.grishko188.pointofinterest.features.categories.ui.models.DetailedCategoriesUiState
17+ import org.junit.After
18+ import org.junit.Before
19+ import org.junit.Rule
20+ import org.junit.Test
21+ import org.junit.runner.RunWith
22+ import org.mockito.Mock
23+ import org.mockito.Mockito
24+ import org.mockito.Mockito.times
25+ import org.mockito.Mockito.verify
26+ import org.mockito.MockitoAnnotations
27+ import org.mockito.junit.MockitoJUnit
28+ import org.robolectric.RobolectricTestRunner
29+ import org.robolectric.annotation.Config
30+ import org.robolectric.shadows.ShadowLog
31+
32+ @RunWith(RobolectricTestRunner ::class )
33+ @Config(instrumentedPackages = [" androidx.loader.content" ])
34+ class CategoriesDetailedScreenUiTest {
35+
36+ @get:Rule(order = 0 )
37+ val composeTestRule = createComposeRule()
38+
39+ @get:Rule(order = 1 )
40+ var mockRule = MockitoJUnit .rule()
41+
42+ @Mock
43+ lateinit var onTextChanged: (TextFieldValue ) -> Unit
44+
45+ @Mock
46+ lateinit var onColorSelected: (Color ) -> Unit
47+
48+ @Mock
49+ lateinit var onSave: (String , Color ) -> Unit
50+
51+ @Mock
52+ lateinit var onUpdate: (CategoryUiModel , String , Color ) -> Unit
53+
54+ @Before
55+ @Throws(Exception ::class )
56+ fun setUp () {
57+ MockitoAnnotations .openMocks(this )
58+ ShadowLog .stream = System .out
59+ }
60+
61+ @After
62+ fun teardown () {
63+ Mockito .validateMockitoUsage()
64+ }
65+
66+ @OptIn(ExperimentalComposeUiApi ::class )
67+ @Test
68+ fun `test CategoriesDetailedContent displays progress when uiState is DetailedCategoriesUiState_Loading` () {
69+ composeTestRule.setContent {
70+ val focusRequester = remember { FocusRequester () }
71+
72+ CategoriesDetailedContent (
73+ uiState = DetailedCategoriesUiState .Loading ,
74+ selectedColor = Color .Red ,
75+ focusRequester = focusRequester,
76+ keyboardController = LocalSoftwareKeyboardController .current,
77+ textFieldValue = TextFieldValue (" " ),
78+ onTextChanged = onTextChanged,
79+ onColorSelected = onColorSelected,
80+ onSave = onSave,
81+ onUpdate = onUpdate
82+ )
83+ }
84+
85+ composeTestRule.onNodeWithTag(" progress_view" )
86+ .assertExists()
87+ .assertIsDisplayed()
88+ }
89+
90+ @OptIn(ExperimentalComposeUiApi ::class )
91+ @Test
92+ fun `test CategoriesDetailedContent displays CategoriesDetailedSuccessContent when uiState is DetailedCategoriesUiState_Success` () {
93+ composeTestRule.setContent {
94+ val focusRequester = remember { FocusRequester () }
95+
96+ CategoriesDetailedContent (
97+ uiState = DetailedCategoriesUiState .Success (null ),
98+ selectedColor = Color .Red ,
99+ focusRequester = focusRequester,
100+ keyboardController = LocalSoftwareKeyboardController .current,
101+ textFieldValue = TextFieldValue (" " ),
102+ onTextChanged = onTextChanged,
103+ onColorSelected = onColorSelected,
104+ onSave = onSave,
105+ onUpdate = onUpdate
106+ )
107+ }
108+
109+ composeTestRule.onNodeWithTag(" progress_view" )
110+ .assertDoesNotExist()
111+
112+ composeTestRule.onNodeWithTag(" detailed_category_content" )
113+ .assertExists()
114+ .assertIsDisplayed()
115+ }
116+
117+ @OptIn(ExperimentalComposeUiApi ::class )
118+ @Test
119+ fun `test CategoriesDetailedSuccessContent empty screen behaviour when uiState is DetailedCategoriesUiState_Success and selectedCategory is null` () {
120+ whenever(onTextChanged(anyNonNull())).thenReturn(Unit )
121+ whenever(onColorSelected(anyNonNull())).thenReturn(Unit )
122+
123+ composeTestRule.setContent {
124+ val focusRequester = remember { FocusRequester () }
125+
126+ CategoriesDetailedContent (
127+ uiState = DetailedCategoriesUiState .Success (null ),
128+ selectedColor = Color .Transparent ,
129+ focusRequester = focusRequester,
130+ keyboardController = LocalSoftwareKeyboardController .current,
131+ textFieldValue = TextFieldValue (" " ),
132+ onTextChanged = onTextChanged,
133+ onColorSelected = onColorSelected,
134+ onSave = onSave,
135+ onUpdate = onUpdate
136+ )
137+ }
138+
139+ composeTestRule.onNodeWithContentDescription(" Clear text icon" )
140+ .assertDoesNotExist()
141+
142+ composeTestRule.onNodeWithText(" Save" , ignoreCase = true )
143+ .assertExists()
144+ .assertIsDisplayed()
145+ .assertIsNotEnabled()
146+
147+ composeTestRule.onNodeWithTag(" grid_color_pallete" )
148+ .assertExists()
149+ .assertIsDisplayed()
150+ .onChildren()
151+ .filter(SemanticsMatcher .keyIsDefined(SemanticsProperties .HorizontalScrollAxisRange ))
152+ .onFirst()
153+ .onChildren()
154+ .filter(SemanticsMatcher .expectValue(SemanticsProperties .Role , Role .Button ))
155+ .assertAll(SemanticsMatcher .expectValue(SemanticsProperties .Selected , false ))
156+ .onFirst()
157+ .assertHasClickAction()
158+ .performClick()
159+
160+ composeTestRule.onNodeWithTag(" original_selected_color_item" )
161+ .assertDoesNotExist()
162+
163+ composeTestRule.onNode(SemanticsMatcher .keyIsDefined(SemanticsProperties .EditableText ))
164+ .assertExists()
165+ .assertIsDisplayed()
166+ .performTextInput(" Test" )
167+
168+ verify(onColorSelected, times(1 )).invoke(anyNonNull())
169+ verify(onTextChanged, times(1 )).invoke(anyNonNull())
170+ }
171+ }
0 commit comments