blob: 6e5f776321cdf6143ced14e777d9aa1e0f014e8b [file] [log] [blame]
Catherine Liang6f794ed2023-01-05 16:12:24 +00001/*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17package com.android.customization.model.picker.color.ui.viewmodel
18
19import android.content.Context
20import androidx.test.filters.SmallTest
21import androidx.test.platform.app.InstrumentationRegistry
22import com.android.customization.picker.color.data.repository.FakeColorPickerRepository
23import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
24import com.android.customization.picker.color.shared.model.ColorType
25import com.android.customization.picker.color.ui.viewmodel.ColorOptionViewModel
26import com.android.customization.picker.color.ui.viewmodel.ColorPickerViewModel
27import com.android.customization.picker.color.ui.viewmodel.ColorTypeViewModel
28import com.android.wallpaper.testing.collectLastValue
29import com.google.common.truth.Truth.assertThat
30import com.google.common.truth.Truth.assertWithMessage
31import kotlinx.coroutines.ExperimentalCoroutinesApi
32import kotlinx.coroutines.test.runTest
33import org.junit.Before
34import org.junit.Test
35import org.junit.runner.RunWith
36import org.junit.runners.JUnit4
37
38@OptIn(ExperimentalCoroutinesApi::class)
39@SmallTest
40@RunWith(JUnit4::class)
41class ColorPickerViewModelTest {
42 private lateinit var underTest: ColorPickerViewModel
43
44 private lateinit var context: Context
45
46 @Before
47 fun setUp() {
48 context = InstrumentationRegistry.getInstrumentation().targetContext
49
50 underTest =
51 ColorPickerViewModel.Factory(
52 context = context,
53 interactor =
54 ColorPickerInteractor(
55 repository = FakeColorPickerRepository(context = context),
56 ),
57 )
58 .create(ColorPickerViewModel::class.java)
59 }
60
61 @Test
Catherine Liang68e0a822023-01-26 20:22:58 +000062 fun `Select a color section color`() = runTest {
63 val colorSectionOptions = collectLastValue(underTest.colorSectionOptions)
64
65 assertColorOptionUiState(colorOptions = colorSectionOptions(), selectedColorOptionIndex = 0)
66
67 colorSectionOptions()?.get(2)?.onClick?.invoke()
68 assertColorOptionUiState(colorOptions = colorSectionOptions(), selectedColorOptionIndex = 2)
69
70 colorSectionOptions()?.get(4)?.onClick?.invoke()
71 assertColorOptionUiState(colorOptions = colorSectionOptions(), selectedColorOptionIndex = 4)
72 }
73
74 @Test
Catherine Liang6f794ed2023-01-05 16:12:24 +000075 fun `Select a preset color`() = runTest {
76 val colorTypes = collectLastValue(underTest.colorTypes)
77 val colorOptions = collectLastValue(underTest.colorOptions)
78
79 // Initially, the wallpaper color tab should be selected
80 assertPickerUiState(
81 colorTypes = colorTypes(),
82 colorOptions = colorOptions(),
83 selectedColorTypeText = "Wallpaper colors",
84 selectedColorOptionIndex = 0
85 )
86
87 // Select "Basic colors" tab
88 colorTypes()?.get(ColorType.BASIC_COLOR)?.onClick?.invoke()
89 assertPickerUiState(
90 colorTypes = colorTypes(),
91 colorOptions = colorOptions(),
92 selectedColorTypeText = "Basic colors",
93 selectedColorOptionIndex = -1
94 )
95
96 // Select a color option
97 colorOptions()?.get(2)?.onClick?.invoke()
98
99 // Check original option is no longer selected
100 colorTypes()?.get(ColorType.WALLPAPER_COLOR)?.onClick?.invoke()
101 assertPickerUiState(
102 colorTypes = colorTypes(),
103 colorOptions = colorOptions(),
104 selectedColorTypeText = "Wallpaper colors",
105 selectedColorOptionIndex = -1
106 )
107
108 // Check new option is selected
109 colorTypes()?.get(ColorType.BASIC_COLOR)?.onClick?.invoke()
110 assertPickerUiState(
111 colorTypes = colorTypes(),
112 colorOptions = colorOptions(),
113 selectedColorTypeText = "Basic colors",
114 selectedColorOptionIndex = 2
115 )
116 }
117
118 /**
119 * Asserts the entire picker UI state is what is expected. This includes the color type tabs and
120 * the color options list.
121 *
122 * @param colorTypes The observed color type view-models, keyed by ColorType
123 * @param colorOptions The observed color options
124 * @param selectedColorTypeText The text of the color type that's expected to be selected
125 * @param selectedColorOptionIndex The index of the color option that's expected to be selected,
126 * -1 stands for no color option should be selected
127 */
128 private fun assertPickerUiState(
129 colorTypes: Map<ColorType, ColorTypeViewModel>?,
130 colorOptions: List<ColorOptionViewModel>?,
131 selectedColorTypeText: String,
132 selectedColorOptionIndex: Int,
133 ) {
134 assertColorTypeTabUiState(
135 colorTypes = colorTypes,
136 colorTypeId = ColorType.WALLPAPER_COLOR,
137 isSelected = "Wallpaper colors" == selectedColorTypeText,
138 )
139 assertColorTypeTabUiState(
140 colorTypes = colorTypes,
141 colorTypeId = ColorType.BASIC_COLOR,
142 isSelected = "Basic colors" == selectedColorTypeText,
143 )
Catherine Liang68e0a822023-01-26 20:22:58 +0000144 assertColorOptionUiState(colorOptions, selectedColorOptionIndex)
145 }
Catherine Liang6f794ed2023-01-05 16:12:24 +0000146
Catherine Liang68e0a822023-01-26 20:22:58 +0000147 /**
148 * Asserts the picker section UI state is what is expected.
149 *
150 * @param colorOptions The observed color options
151 * @param selectedColorOptionIndex The index of the color option that's expected to be selected,
152 * -1 stands for no color option should be selected
153 */
154 private fun assertColorOptionUiState(
155 colorOptions: List<ColorOptionViewModel>?,
156 selectedColorOptionIndex: Int,
157 ) {
Catherine Liang6f794ed2023-01-05 16:12:24 +0000158 var foundSelectedColorOption = false
159 assertThat(colorOptions).isNotNull()
160 if (colorOptions != null) {
161 for (i in colorOptions.indices) {
162 val colorOptionHasSelectedIndex = i == selectedColorOptionIndex
163 assertWithMessage(
164 "Expected color option with index \"${i}\" to have" +
165 " isSelected=$colorOptionHasSelectedIndex but it was" +
166 " ${colorOptions[i].isSelected}, num options: ${colorOptions.size}"
167 )
168 .that(colorOptions[i].isSelected)
169 .isEqualTo(colorOptionHasSelectedIndex)
170 foundSelectedColorOption = foundSelectedColorOption || colorOptionHasSelectedIndex
171 }
172 if (selectedColorOptionIndex == -1) {
173 assertWithMessage(
174 "Expected no color options to be selected, but a color option is" +
175 " selected"
176 )
177 .that(foundSelectedColorOption)
178 .isFalse()
179 } else {
180 assertWithMessage(
181 "Expected a color option to be selected, but no color option is" +
182 " selected"
183 )
184 .that(foundSelectedColorOption)
185 .isTrue()
186 }
187 }
188 }
189
190 /**
191 * Asserts that a color type tab has the correct UI state.
192 *
193 * @param colorTypes The observed color type view-models, keyed by ColorType enum
194 * @param colorTypeId the ID of the color type to assert
195 * @param isSelected Whether that color type should be selected
196 */
197 private fun assertColorTypeTabUiState(
198 colorTypes: Map<ColorType, ColorTypeViewModel>?,
199 colorTypeId: ColorType,
200 isSelected: Boolean,
201 ) {
202 val viewModel =
203 colorTypes?.get(colorTypeId) ?: error("No color type with ID \"$colorTypeId\"!")
204 assertThat(viewModel.isSelected).isEqualTo(isSelected)
205 }
206}