Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2022 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 | */ |
| 17 | |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 18 | package com.android.customization.model.picker.quickaffordance.ui.viewmodel |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 19 | |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 20 | import android.content.Context |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 21 | import androidx.test.filters.SmallTest |
| 22 | import androidx.test.platform.app.InstrumentationRegistry |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 23 | import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository |
| 24 | import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 25 | import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordanceSnapshotRestorer |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 26 | import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel |
| 27 | import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceSlotViewModel |
| 28 | import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceSummaryViewModel |
| 29 | import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceViewModel |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 30 | import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots |
| 31 | import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 32 | import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 33 | import com.android.wallpaper.picker.undo.data.repository.UndoRepository |
| 34 | import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor |
| 35 | import com.android.wallpaper.testing.FAKE_RESTORERS |
| 36 | import com.android.wallpaper.testing.collectLastValue |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 37 | import com.google.common.truth.Truth.assertThat |
| 38 | import com.google.common.truth.Truth.assertWithMessage |
| 39 | import kotlinx.coroutines.Dispatchers |
| 40 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 41 | import kotlinx.coroutines.runBlocking |
| 42 | import kotlinx.coroutines.test.StandardTestDispatcher |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 43 | import kotlinx.coroutines.test.TestScope |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 44 | import kotlinx.coroutines.test.resetMain |
| 45 | import kotlinx.coroutines.test.runTest |
| 46 | import kotlinx.coroutines.test.setMain |
| 47 | import org.junit.After |
| 48 | import org.junit.Before |
| 49 | import org.junit.Test |
| 50 | import org.junit.runner.RunWith |
| 51 | import org.junit.runners.JUnit4 |
| 52 | |
| 53 | @OptIn(ExperimentalCoroutinesApi::class) |
| 54 | @SmallTest |
| 55 | @RunWith(JUnit4::class) |
| 56 | class KeyguardQuickAffordancePickerViewModelTest { |
| 57 | |
| 58 | private lateinit var underTest: KeyguardQuickAffordancePickerViewModel |
| 59 | |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 60 | private lateinit var context: Context |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 61 | private lateinit var testScope: TestScope |
| 62 | private lateinit var client: FakeKeyguardQuickAffordanceProviderClient |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 63 | private lateinit var quickAffordanceInteractor: KeyguardQuickAffordancePickerInteractor |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 64 | |
| 65 | @Before |
| 66 | fun setUp() { |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 67 | context = InstrumentationRegistry.getInstrumentation().targetContext |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 68 | val testDispatcher = StandardTestDispatcher() |
| 69 | testScope = TestScope(testDispatcher) |
| 70 | Dispatchers.setMain(testDispatcher) |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 71 | client = FakeKeyguardQuickAffordanceProviderClient() |
| 72 | |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 73 | quickAffordanceInteractor = |
| 74 | KeyguardQuickAffordancePickerInteractor( |
| 75 | repository = |
| 76 | KeyguardQuickAffordancePickerRepository( |
| 77 | client = client, |
| 78 | backgroundDispatcher = testDispatcher, |
| 79 | ), |
| 80 | client = client, |
| 81 | snapshotRestorer = { |
| 82 | KeyguardQuickAffordanceSnapshotRestorer( |
| 83 | interactor = quickAffordanceInteractor, |
| 84 | client = client, |
| 85 | ) |
| 86 | .apply { runBlocking { setUpSnapshotRestorer {} } } |
| 87 | }, |
| 88 | ) |
| 89 | val undoInteractor = |
| 90 | UndoInteractor( |
| 91 | scope = testScope.backgroundScope, |
| 92 | repository = UndoRepository(), |
| 93 | restorerByOwnerId = FAKE_RESTORERS, |
| 94 | ) |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 95 | underTest = |
| 96 | KeyguardQuickAffordancePickerViewModel.Factory( |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 97 | context = context, |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 98 | quickAffordanceInteractor = quickAffordanceInteractor, |
| 99 | undoInteractor = undoInteractor, |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 100 | ) |
| 101 | .create(KeyguardQuickAffordancePickerViewModel::class.java) |
| 102 | } |
| 103 | |
| 104 | @After |
| 105 | fun tearDown() { |
| 106 | Dispatchers.resetMain() |
| 107 | } |
| 108 | |
| 109 | @Test |
| 110 | fun `Select an affordance for each side`() = |
| 111 | testScope.runTest { |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 112 | val slots = collectLastValue(underTest.slots) |
| 113 | val quickAffordances = collectLastValue(underTest.quickAffordances) |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 114 | |
| 115 | // Initially, the first slot is selected with the "none" affordance selected. |
| 116 | assertPickerUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 117 | slots = slots(), |
| 118 | affordances = quickAffordances(), |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 119 | selectedSlotText = "Left button", |
| 120 | selectedAffordanceText = "None", |
| 121 | ) |
| 122 | assertPreviewUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 123 | slots = slots(), |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 124 | expectedAffordanceNameBySlotId = |
| 125 | mapOf( |
| 126 | KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to null, |
| 127 | KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to null, |
| 128 | ), |
| 129 | ) |
| 130 | |
| 131 | // Select "affordance 1" for the first slot. |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 132 | quickAffordances()?.get(1)?.onClicked?.invoke() |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 133 | assertPickerUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 134 | slots = slots(), |
| 135 | affordances = quickAffordances(), |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 136 | selectedSlotText = "Left button", |
| 137 | selectedAffordanceText = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1, |
| 138 | ) |
| 139 | assertPreviewUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 140 | slots = slots(), |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 141 | expectedAffordanceNameBySlotId = |
| 142 | mapOf( |
| 143 | KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to |
| 144 | FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1, |
| 145 | KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to null, |
| 146 | ), |
| 147 | ) |
| 148 | |
| 149 | // Select an affordance for the second slot. |
| 150 | // First, switch to the second slot: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 151 | slots()?.get(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END)?.onClicked?.invoke() |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 152 | // Second, select the "affordance 3" affordance: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 153 | quickAffordances()?.get(3)?.onClicked?.invoke() |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 154 | assertPickerUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 155 | slots = slots(), |
| 156 | affordances = quickAffordances(), |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 157 | selectedSlotText = "Right button", |
| 158 | selectedAffordanceText = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_3, |
| 159 | ) |
| 160 | assertPreviewUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 161 | slots = slots(), |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 162 | expectedAffordanceNameBySlotId = |
| 163 | mapOf( |
| 164 | KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to |
| 165 | FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1, |
| 166 | KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to |
| 167 | FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_3, |
| 168 | ), |
| 169 | ) |
| 170 | |
| 171 | // Select a different affordance for the second slot. |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 172 | quickAffordances()?.get(2)?.onClicked?.invoke() |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 173 | assertPickerUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 174 | slots = slots(), |
| 175 | affordances = quickAffordances(), |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 176 | selectedSlotText = "Right button", |
| 177 | selectedAffordanceText = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2, |
| 178 | ) |
| 179 | assertPreviewUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 180 | slots = slots(), |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 181 | expectedAffordanceNameBySlotId = |
| 182 | mapOf( |
| 183 | KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to |
| 184 | FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1, |
| 185 | KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to |
| 186 | FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2, |
| 187 | ), |
| 188 | ) |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 189 | } |
| 190 | |
| 191 | @Test |
| 192 | fun `Unselect - AKA selecting the none affordance - on one side`() = |
| 193 | testScope.runTest { |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 194 | val slots = collectLastValue(underTest.slots) |
| 195 | val quickAffordances = collectLastValue(underTest.quickAffordances) |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 196 | |
| 197 | // Select "affordance 1" for the first slot. |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 198 | quickAffordances()?.get(1)?.onClicked?.invoke() |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 199 | // Select an affordance for the second slot. |
| 200 | // First, switch to the second slot: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 201 | slots()?.get(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END)?.onClicked?.invoke() |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 202 | // Second, select the "affordance 3" affordance: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 203 | quickAffordances()?.get(3)?.onClicked?.invoke() |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 204 | |
| 205 | // Switch back to the first slot: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 206 | slots()?.get(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)?.onClicked?.invoke() |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 207 | // Select the "none" affordance, which is always in position 0: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 208 | quickAffordances()?.get(0)?.onClicked?.invoke() |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 209 | |
| 210 | assertPickerUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 211 | slots = slots(), |
| 212 | affordances = quickAffordances(), |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 213 | selectedSlotText = "Left button", |
| 214 | selectedAffordanceText = "None", |
| 215 | ) |
| 216 | assertPreviewUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 217 | slots = slots(), |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 218 | expectedAffordanceNameBySlotId = |
| 219 | mapOf( |
| 220 | KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to null, |
| 221 | KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to |
| 222 | FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_3, |
| 223 | ), |
| 224 | ) |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 225 | } |
| 226 | |
| 227 | @Test |
| 228 | fun `Show enablement dialog when selecting a disabled affordance`() = |
| 229 | testScope.runTest { |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 230 | val slots = collectLastValue(underTest.slots) |
| 231 | val quickAffordances = collectLastValue(underTest.quickAffordances) |
| 232 | val dialog = collectLastValue(underTest.dialog) |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 233 | |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 234 | val enablementInstructions = listOf("header", "enablementInstructions") |
| 235 | val enablementActionText = "enablementActionText" |
| 236 | val packageName = "packageName" |
| 237 | val action = "action" |
| 238 | val enablementActionComponentName = "$packageName/$action" |
| 239 | // Lets add a disabled affordance to the picker: |
| 240 | val affordanceIndex = |
| 241 | client.addAffordance( |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 242 | KeyguardQuickAffordanceProviderClient.Affordance( |
| 243 | id = "disabled", |
| 244 | name = "disabled", |
| 245 | iconResourceId = 0, |
| 246 | isEnabled = false, |
| 247 | enablementInstructions = enablementInstructions, |
| 248 | enablementActionText = enablementActionText, |
| 249 | enablementActionComponentName = enablementActionComponentName, |
| 250 | ) |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 251 | ) |
| 252 | |
| 253 | // Lets try to select that disabled affordance: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 254 | quickAffordances()?.get(affordanceIndex + 1)?.onClicked?.invoke() |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 255 | |
| 256 | // We expect there to be a dialog that should be shown: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 257 | assertThat(dialog()?.instructionHeader).isEqualTo(enablementInstructions[0]) |
| 258 | assertThat(dialog()?.instructions) |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 259 | .isEqualTo(enablementInstructions.subList(1, enablementInstructions.size)) |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 260 | assertThat(dialog()?.actionText).isEqualTo(enablementActionText) |
| 261 | assertThat(dialog()?.intent?.`package`).isEqualTo(packageName) |
| 262 | assertThat(dialog()?.intent?.action).isEqualTo(action) |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 263 | |
| 264 | // Once we report that the dialog has been dismissed by the user, we expect there to be |
| 265 | // no |
| 266 | // dialog to be shown: |
| 267 | underTest.onDialogDismissed() |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 268 | assertThat(dialog()).isNull() |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 269 | } |
| 270 | |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 271 | @Test |
| 272 | fun `summary - affordance selected in both bottom-start and bottom-end`() = |
| 273 | testScope.runTest { |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 274 | val slots = collectLastValue(underTest.slots) |
| 275 | val quickAffordances = collectLastValue(underTest.quickAffordances) |
| 276 | val summary = collectLastValue(underTest.summary) |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 277 | |
| 278 | // Select "affordance 1" for the first slot. |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 279 | quickAffordances()?.get(1)?.onClicked?.invoke() |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 280 | // Select an affordance for the second slot. |
| 281 | // First, switch to the second slot: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 282 | slots()?.get(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END)?.onClicked?.invoke() |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 283 | // Second, select the "affordance 3" affordance: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 284 | quickAffordances()?.get(3)?.onClicked?.invoke() |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 285 | |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 286 | assertThat(summary()) |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 287 | .isEqualTo( |
| 288 | KeyguardQuickAffordanceSummaryViewModel( |
| 289 | description = |
| 290 | "${FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1}," + |
| 291 | " ${FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_3}", |
| 292 | icon1 = FakeKeyguardQuickAffordanceProviderClient.ICON_1, |
| 293 | icon2 = FakeKeyguardQuickAffordanceProviderClient.ICON_3, |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 294 | ) |
| 295 | ) |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 296 | } |
| 297 | |
| 298 | @Test |
| 299 | fun `summary - affordance selected only on bottom-start`() = |
| 300 | testScope.runTest { |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 301 | val slots = collectLastValue(underTest.slots) |
| 302 | val quickAffordances = collectLastValue(underTest.quickAffordances) |
| 303 | val summary = collectLastValue(underTest.summary) |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 304 | |
| 305 | // Select "affordance 1" for the first slot. |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 306 | quickAffordances()?.get(1)?.onClicked?.invoke() |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 307 | |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 308 | assertThat(summary()) |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 309 | .isEqualTo( |
| 310 | KeyguardQuickAffordanceSummaryViewModel( |
| 311 | description = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1, |
| 312 | icon1 = FakeKeyguardQuickAffordanceProviderClient.ICON_1, |
| 313 | icon2 = null, |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 314 | ) |
| 315 | ) |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 316 | } |
| 317 | |
| 318 | @Test |
| 319 | fun `summary - affordance selected only on bottom-end`() = |
| 320 | testScope.runTest { |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 321 | val slots = collectLastValue(underTest.slots) |
| 322 | val quickAffordances = collectLastValue(underTest.quickAffordances) |
| 323 | val summary = collectLastValue(underTest.summary) |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 324 | |
| 325 | // Select an affordance for the second slot. |
| 326 | // First, switch to the second slot: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 327 | slots()?.get(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END)?.onClicked?.invoke() |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 328 | // Second, select the "affordance 3" affordance: |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 329 | quickAffordances()?.get(3)?.onClicked?.invoke() |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 330 | |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 331 | assertThat(summary()) |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 332 | .isEqualTo( |
| 333 | KeyguardQuickAffordanceSummaryViewModel( |
| 334 | description = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_3, |
| 335 | icon1 = null, |
| 336 | icon2 = FakeKeyguardQuickAffordanceProviderClient.ICON_3, |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 337 | ) |
| 338 | ) |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 339 | } |
| 340 | |
| 341 | @Test |
| 342 | fun `summary - no affordances selected`() = |
| 343 | testScope.runTest { |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 344 | val slots = collectLastValue(underTest.slots) |
| 345 | val quickAffordances = collectLastValue(underTest.quickAffordances) |
| 346 | val summary = collectLastValue(underTest.summary) |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 347 | |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 348 | assertThat(summary()?.description).isEqualTo("None") |
| 349 | assertThat(summary()?.icon1).isNotNull() |
| 350 | assertThat(summary()?.icon2).isNull() |
Alejandro Nijamkin | abda67b | 2022-11-30 14:34:56 -0800 | [diff] [blame] | 351 | } |
| 352 | |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 353 | /** |
| 354 | * Asserts the entire picker UI state is what is expected. This includes the slot tabs and the |
| 355 | * affordance list. |
| 356 | * |
| 357 | * @param slots The observed slot view-models, keyed by slot ID |
| 358 | * @param affordances The observed affordances |
| 359 | * @param selectedSlotText The text of the slot that's expected to be selected |
| 360 | * @param selectedAffordanceText The text of the affordance that's expected to be selected |
| 361 | */ |
| 362 | private fun assertPickerUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 363 | slots: Map<String, KeyguardQuickAffordanceSlotViewModel>?, |
| 364 | affordances: List<KeyguardQuickAffordanceViewModel>?, |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 365 | selectedSlotText: String, |
| 366 | selectedAffordanceText: String, |
| 367 | ) { |
| 368 | assertSlotTabUiState( |
| 369 | slots = slots, |
| 370 | slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, |
| 371 | isSelected = "Left button" == selectedSlotText, |
| 372 | ) |
| 373 | assertSlotTabUiState( |
| 374 | slots = slots, |
| 375 | slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, |
| 376 | isSelected = "Right button" == selectedSlotText, |
| 377 | ) |
| 378 | |
| 379 | var foundSelectedAffordance = false |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 380 | assertThat(affordances).isNotNull() |
| 381 | affordances?.forEach { affordance -> |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 382 | val nameMatchesSelectedName = affordance.contentDescription == selectedAffordanceText |
| 383 | assertWithMessage( |
| 384 | "Expected affordance with name \"${affordance.contentDescription}\" to have" + |
| 385 | " isSelected=$nameMatchesSelectedName but it was ${affordance.isSelected}" |
| 386 | ) |
| 387 | .that(affordance.isSelected) |
| 388 | .isEqualTo(nameMatchesSelectedName) |
| 389 | foundSelectedAffordance = foundSelectedAffordance || nameMatchesSelectedName |
| 390 | } |
| 391 | assertWithMessage("No affordance is selected!").that(foundSelectedAffordance).isTrue() |
| 392 | } |
| 393 | |
| 394 | /** |
| 395 | * Asserts that a slot tab has the correct UI state. |
| 396 | * |
| 397 | * @param slots The observed slot view-models, keyed by slot ID |
| 398 | * @param slotId the ID of the slot to assert |
| 399 | * @param isSelected Whether that slot should be selected |
| 400 | */ |
| 401 | private fun assertSlotTabUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 402 | slots: Map<String, KeyguardQuickAffordanceSlotViewModel>?, |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 403 | slotId: String, |
| 404 | isSelected: Boolean, |
| 405 | ) { |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 406 | val viewModel = slots?.get(slotId) ?: error("No slot with ID \"$slotId\"!") |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 407 | assertThat(viewModel.isSelected).isEqualTo(isSelected) |
| 408 | } |
| 409 | |
| 410 | /** |
| 411 | * Asserts the UI state of the preview. |
| 412 | * |
| 413 | * @param slots The observed slot view-models, keyed by slot ID |
| 414 | * @param expectedAffordanceNameBySlotId The expected name of the selected affordance for each |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 415 | * slot ID or `null` if it's expected for there to be no affordance for that slot in the |
| 416 | * preview |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 417 | */ |
| 418 | private fun assertPreviewUiState( |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 419 | slots: Map<String, KeyguardQuickAffordanceSlotViewModel>?, |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 420 | expectedAffordanceNameBySlotId: Map<String, String?>, |
| 421 | ) { |
Alejandro Nijamkin | c27b1d3 | 2022-12-21 15:27:35 -0800 | [diff] [blame^] | 422 | assertThat(slots).isNotNull() |
| 423 | slots?.forEach { (slotId, slotViewModel) -> |
Alejandro Nijamkin | 0f02b08 | 2022-11-24 13:43:43 -0800 | [diff] [blame] | 424 | val expectedAffordanceName = expectedAffordanceNameBySlotId[slotId] |
| 425 | val actualAffordanceName = |
| 426 | slotViewModel.selectedQuickAffordances.firstOrNull()?.contentDescription |
| 427 | assertWithMessage( |
| 428 | "At slotId=\"$slotId\", expected affordance=\"$expectedAffordanceName\" but" + |
| 429 | " was \"$actualAffordanceName\"!" |
| 430 | ) |
| 431 | .that(actualAffordanceName) |
| 432 | .isEqualTo(expectedAffordanceName) |
| 433 | } |
| 434 | } |
| 435 | } |