Merge pull request #2744 from element-hq/feature/bma/testPreferenceTextField
Add Ui test on DeveloperSettingsView
This commit is contained in:
commit
41de39bac3
3 changed files with 153 additions and 11 deletions
|
|
@ -25,22 +25,36 @@ open class DeveloperSettingsStateProvider : PreviewParameterProvider<DeveloperSe
|
||||||
override val values: Sequence<DeveloperSettingsState>
|
override val values: Sequence<DeveloperSettingsState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aDeveloperSettingsState(),
|
aDeveloperSettingsState(),
|
||||||
aDeveloperSettingsState().copy(clearCacheAction = AsyncData.Loading()),
|
aDeveloperSettingsState(
|
||||||
aDeveloperSettingsState().copy(
|
clearCacheAction = AsyncData.Loading()
|
||||||
customElementCallBaseUrlState = CustomElementCallBaseUrlState(
|
),
|
||||||
|
aDeveloperSettingsState(
|
||||||
|
customElementCallBaseUrlState = aCustomElementCallBaseUrlState(
|
||||||
baseUrl = "https://call.element.ahoy",
|
baseUrl = "https://call.element.ahoy",
|
||||||
defaultUrl = "https://call.element.io",
|
|
||||||
validator = { true }
|
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aDeveloperSettingsState() = DeveloperSettingsState(
|
fun aDeveloperSettingsState(
|
||||||
|
clearCacheAction: AsyncData<Unit> = AsyncData.Uninitialized,
|
||||||
|
customElementCallBaseUrlState: CustomElementCallBaseUrlState = aCustomElementCallBaseUrlState(),
|
||||||
|
eventSink: (DeveloperSettingsEvents) -> Unit = {},
|
||||||
|
) = DeveloperSettingsState(
|
||||||
features = aFeatureUiModelList(),
|
features = aFeatureUiModelList(),
|
||||||
rageshakeState = aRageshakePreferencesState(),
|
rageshakeState = aRageshakePreferencesState(),
|
||||||
cacheSize = AsyncData.Success("1.2 MB"),
|
cacheSize = AsyncData.Success("1.2 MB"),
|
||||||
clearCacheAction = AsyncData.Uninitialized,
|
clearCacheAction = clearCacheAction,
|
||||||
customElementCallBaseUrlState = CustomElementCallBaseUrlState(baseUrl = null, defaultUrl = "https://call.element.io", validator = { true }),
|
customElementCallBaseUrlState = customElementCallBaseUrlState,
|
||||||
eventSink = {}
|
eventSink = eventSink,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun aCustomElementCallBaseUrlState(
|
||||||
|
baseUrl: String? = null,
|
||||||
|
defaultUrl: String = "https://call.element.io",
|
||||||
|
validator: (String?) -> Boolean = { true },
|
||||||
|
) = CustomElementCallBaseUrlState(
|
||||||
|
baseUrl = baseUrl,
|
||||||
|
defaultUrl = defaultUrl,
|
||||||
|
validator = validator,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.preferences.impl.developer
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||||
|
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||||
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
|
import androidx.compose.ui.test.performClick
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import io.element.android.features.preferences.impl.R
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||||
|
import io.element.android.tests.testutils.EventsRecorder
|
||||||
|
import io.element.android.tests.testutils.clickOn
|
||||||
|
import io.element.android.tests.testutils.ensureCalledOnce
|
||||||
|
import io.element.android.tests.testutils.pressBack
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.rules.TestRule
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class DeveloperSettingsViewTest {
|
||||||
|
@get:Rule
|
||||||
|
val rule = createAndroidComposeRule<ComponentActivity>()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `clicking on back invokes the expected callback`() {
|
||||||
|
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>(expectEvents = false)
|
||||||
|
ensureCalledOnce {
|
||||||
|
rule.setDeveloperSettingsView(
|
||||||
|
state = aDeveloperSettingsState(
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
onBackPressed = it
|
||||||
|
)
|
||||||
|
rule.pressBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `clicking on element call url open the dialogs and submit emits the expected event`() {
|
||||||
|
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>()
|
||||||
|
rule.setDeveloperSettingsView(
|
||||||
|
state = aDeveloperSettingsState(
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
)
|
||||||
|
rule.clickOn(R.string.screen_advanced_settings_element_call_base_url)
|
||||||
|
rule.clickOn(CommonStrings.action_ok)
|
||||||
|
eventsRecorder.assertSingle(DeveloperSettingsEvents.SetCustomElementCallBaseUrl("https://call.element.io"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Config(qualifiers = "h1024dp")
|
||||||
|
@Test
|
||||||
|
fun `clicking on open showkase invokes the expected callback`() {
|
||||||
|
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>(expectEvents = false)
|
||||||
|
ensureCalledOnce {
|
||||||
|
rule.setDeveloperSettingsView(
|
||||||
|
state = aDeveloperSettingsState(
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
onOpenShowkase = it
|
||||||
|
)
|
||||||
|
rule.onNodeWithText("Open Showkase browser").performClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `clicking on configure tracing invokes the expected callback`() {
|
||||||
|
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>(expectEvents = false)
|
||||||
|
ensureCalledOnce {
|
||||||
|
rule.setDeveloperSettingsView(
|
||||||
|
state = aDeveloperSettingsState(
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
onOpenConfigureTracing = it
|
||||||
|
)
|
||||||
|
rule.onNodeWithText("Configure tracing").performClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Config(qualifiers = "h1024dp")
|
||||||
|
@Test
|
||||||
|
fun `clicking on clear cache emits the expected event`() {
|
||||||
|
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>()
|
||||||
|
rule.setDeveloperSettingsView(
|
||||||
|
state = aDeveloperSettingsState(
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
)
|
||||||
|
rule.onNodeWithText("Clear cache").performClick()
|
||||||
|
eventsRecorder.assertSingle(DeveloperSettingsEvents.ClearCache)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setDeveloperSettingsView(
|
||||||
|
state: DeveloperSettingsState,
|
||||||
|
onOpenShowkase: () -> Unit = EnsureNeverCalled(),
|
||||||
|
onOpenConfigureTracing: () -> Unit = EnsureNeverCalled(),
|
||||||
|
onBackPressed: () -> Unit = EnsureNeverCalled()
|
||||||
|
) {
|
||||||
|
setContent {
|
||||||
|
DeveloperSettingsView(
|
||||||
|
state = state,
|
||||||
|
onOpenShowkase = onOpenShowkase,
|
||||||
|
onOpenConfigureTracing = onOpenConfigureTracing,
|
||||||
|
onBackPressed = onBackPressed,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -98,11 +98,11 @@ private fun TextFieldDialog(
|
||||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||||
) {
|
) {
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
var textFieldContents by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
var textFieldContents by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
||||||
mutableStateOf(TextFieldValue(value.orEmpty(), selection = TextRange(value.orEmpty().length)))
|
mutableStateOf(TextFieldValue(value.orEmpty(), selection = TextRange(value.orEmpty().length)))
|
||||||
}
|
}
|
||||||
var error by rememberSaveable { mutableStateOf<String?>(null) }
|
var error by rememberSaveable { mutableStateOf<String?>(null) }
|
||||||
|
var canRequestFocus by rememberSaveable { mutableStateOf(false) }
|
||||||
val canSubmit by remember { derivedStateOf { validation(textFieldContents.text) } }
|
val canSubmit by remember { derivedStateOf { validation(textFieldContents.text) } }
|
||||||
ListDialog(
|
ListDialog(
|
||||||
title = title,
|
title = title,
|
||||||
|
|
@ -128,10 +128,11 @@ private fun TextFieldDialog(
|
||||||
maxLines = maxLines,
|
maxLines = maxLines,
|
||||||
modifier = Modifier.focusRequester(focusRequester),
|
modifier = Modifier.focusRequester(focusRequester),
|
||||||
)
|
)
|
||||||
|
canRequestFocus = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoSelectOnDisplay) {
|
if (autoSelectOnDisplay && canRequestFocus) {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
focusRequester.requestFocus()
|
focusRequester.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue