diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/ChangelogDialogTest.kt similarity index 80% rename from android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt rename to android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/ChangelogDialogTest.kt index b8165f80fc2d..b811209d1ce3 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/ChangelogDialogTest.kt @@ -1,12 +1,12 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.compose.dialog import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.impl.annotations.MockK import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension -import net.mullvad.mullvadvpn.compose.dialog.ChangelogDialog import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.viewmodel.AppInfoViewModel import net.mullvad.mullvadvpn.viewmodel.ChangelogUiState @@ -25,16 +25,19 @@ class ChangelogDialogTest { MockKAnnotations.init(this) } + private fun ComposeContext.initDialog(state: ChangelogUiState, onDismiss: () -> Unit = {}) { + setContentWithTheme { ChangelogDialog(state = state, onDismiss = onDismiss) } + } + @Test fun testShowChangeLogWhenNeeded() = composeExtension.use { // Arrange - setContentWithTheme { - ChangelogDialog( + initDialog( + state = ChangelogUiState(changes = listOf(CHANGELOG_ITEM), version = CHANGELOG_VERSION), - onDismiss = {}, - ) - } + onDismiss = {}, + ) // Check changelog content showed within dialog onNodeWithText(CHANGELOG_ITEM).assertExists() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialogTest.kt index 915db8243887..83dd0c3fdb59 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialogTest.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -30,12 +31,28 @@ class CreateCustomListDialogTest { MockKAnnotations.init(this) } + private fun ComposeContext.initDialog( + state: CreateCustomListUiState = CreateCustomListUiState(), + createCustomList: (String) -> Unit = {}, + onInputChanged: () -> Unit = {}, + onDismiss: () -> Unit = {}, + ) { + setContentWithTheme { + CreateCustomListDialog( + state = state, + createCustomList = createCustomList, + onInputChanged = onInputChanged, + onDismiss = onDismiss, + ) + } + } + @Test fun givenNoErrorShouldShowNoErrorMessage() = composeExtension.use { // Arrange val state = CreateCustomListUiState(error = null) - setContentWithTheme { CreateCustomListDialog(state = state) } + initDialog(state) // Assert onNodeWithText(NAME_EXIST_ERROR_TEXT).assertDoesNotExist() @@ -50,7 +67,7 @@ class CreateCustomListDialogTest { CreateCustomListUiState( error = CreateWithLocationsError.Create(CustomListAlreadyExists) ) - setContentWithTheme { CreateCustomListDialog(state = state) } + initDialog(state) // Assert onNodeWithText(NAME_EXIST_ERROR_TEXT).assertExists() @@ -65,7 +82,7 @@ class CreateCustomListDialogTest { CreateCustomListUiState( error = CreateWithLocationsError.Create(UnknownCustomListError(Throwable())) ) - setContentWithTheme { CreateCustomListDialog(state = state) } + initDialog(state) // Assert onNodeWithText(NAME_EXIST_ERROR_TEXT).assertDoesNotExist() @@ -78,9 +95,7 @@ class CreateCustomListDialogTest { // Arrange val mockedOnDismiss: () -> Unit = mockk(relaxed = true) val state = CreateCustomListUiState() - setContentWithTheme { - CreateCustomListDialog(state = state, onDismiss = mockedOnDismiss) - } + initDialog(state, onDismiss = mockedOnDismiss) // Act onNodeWithText(CANCEL_BUTTON_TEXT).performClick() @@ -95,9 +110,7 @@ class CreateCustomListDialogTest { // Arrange val mockedCreateCustomList: (String) -> Unit = mockk(relaxed = true) val state = CreateCustomListUiState() - setContentWithTheme { - CreateCustomListDialog(state = state, createCustomList = mockedCreateCustomList) - } + initDialog(state, createCustomList = mockedCreateCustomList) // Act onNodeWithText(CREATE_BUTTON_TEXT).performClick() @@ -113,9 +126,7 @@ class CreateCustomListDialogTest { val mockedCreateCustomList: (String) -> Unit = mockk(relaxed = true) val inputText = "NEW LIST" val state = CreateCustomListUiState() - setContentWithTheme { - CreateCustomListDialog(state = state, createCustomList = mockedCreateCustomList) - } + initDialog(state, createCustomList = mockedCreateCustomList) // Act onNodeWithTag(CREATE_CUSTOM_LIST_DIALOG_INPUT_TEST_TAG).performTextInput(inputText) @@ -132,9 +143,7 @@ class CreateCustomListDialogTest { val mockedOnInputChanged: () -> Unit = mockk(relaxed = true) val inputText = "NEW LIST" val state = CreateCustomListUiState() - setContentWithTheme { - CreateCustomListDialog(state = state, onInputChanged = mockedOnInputChanged) - } + initDialog(state, onInputChanged = mockedOnInputChanged) // Act onNodeWithTag(CREATE_CUSTOM_LIST_DIALOG_INPUT_TEST_TAG).performTextInput(inputText) diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialogTest.kt index 7ca4d7f3d4b0..075c6cc36023 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialogTest.kt @@ -1,7 +1,5 @@ package net.mullvad.mullvadvpn.compose.dialog -import android.annotation.SuppressLint -import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -13,6 +11,7 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -36,30 +35,30 @@ class CustomPortDialogTest { MockKAnnotations.init(this) } - @SuppressLint("ComposableNaming") - @Composable - private fun testWireguardCustomPortDialog( + private fun ComposeContext.initDialog( title: String = "", portInput: String = "", isValidInput: Boolean = false, - showResetToDefault: Boolean = false, allowedPortRanges: List = emptyList(), + showResetToDefault: Boolean = false, onInputChanged: (String) -> Unit = { _ -> }, onSavePort: (String) -> Unit = { _ -> }, onResetPort: () -> Unit = {}, onDismiss: () -> Unit = {}, ) { - CustomPortDialog( - title = title, - portInput = portInput, - isValidInput = isValidInput, - showResetToDefault = showResetToDefault, - allowedPortRanges = allowedPortRanges, - onInputChanged = onInputChanged, - onSavePort = onSavePort, - onDismiss = onDismiss, - onResetPort = onResetPort, - ) + setContentWithTheme { + CustomPortDialog( + title = title, + portInput = portInput, + isValidInput = isValidInput, + allowedPortRanges = allowedPortRanges, + showResetToDefault = showResetToDefault, + onInputChanged = onInputChanged, + onSavePort = onSavePort, + onDismiss = onDismiss, + onResetPort = onResetPort, + ) + } } @Test @@ -71,7 +70,17 @@ class CustomPortDialogTest { // Arrange setContentWithTheme { var input by remember { mutableStateOf("") } - testWireguardCustomPortDialog(portInput = input, onInputChanged = { input = it }) + CustomPortDialog( + title = "", + portInput = input, + isValidInput = false, + allowedPortRanges = emptyList(), + showResetToDefault = false, + onInputChanged = { input = it }, + onSavePort = {}, + onDismiss = {}, + onResetPort = {}, + ) } // Act @@ -86,7 +95,7 @@ class CustomPortDialogTest { fun testEmptyInputResultsInSetPortButtonBeingDisabled() = composeExtension.use { // Arrange - setContentWithTheme { testWireguardCustomPortDialog(isValidInput = false) } + initDialog(isValidInput = false) // Assert onNodeWithText("Set port").assertIsNotEnabled() @@ -96,9 +105,7 @@ class CustomPortDialogTest { fun testValidInputResultsInSetPortButtonBeingEnabled() = composeExtension.use { // Arrange - setContentWithTheme { - testWireguardCustomPortDialog(portInput = VALID_CUSTOM_PORT, isValidInput = true) - } + initDialog(portInput = VALID_CUSTOM_PORT, isValidInput = true) // Assert onNodeWithText("Set port").assertIsEnabled() @@ -109,9 +116,7 @@ class CustomPortDialogTest { fun testInvalidInputResultsInSetPortButtonBeingDisabled() = composeExtension.use { // Arrange - setContentWithTheme { - testWireguardCustomPortDialog(portInput = INVALID_CUSTOM_PORT, isValidInput = false) - } + initDialog(portInput = INVALID_CUSTOM_PORT, isValidInput = false) // Assert onNodeWithText("Set port").assertIsNotEnabled() @@ -122,13 +127,11 @@ class CustomPortDialogTest { composeExtension.use { // Arrange val mockedSubmitHandler: (String) -> Unit = mockk(relaxed = true) - setContentWithTheme { - testWireguardCustomPortDialog( - portInput = VALID_CUSTOM_PORT, - isValidInput = true, - onSavePort = mockedSubmitHandler, - ) - } + initDialog( + portInput = VALID_CUSTOM_PORT, + isValidInput = true, + onSavePort = mockedSubmitHandler, + ) // Act onNodeWithText("Set port").assertIsEnabled().performClick() @@ -142,14 +145,12 @@ class CustomPortDialogTest { composeExtension.use { // Arrange val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - testWireguardCustomPortDialog( - portInput = VALID_CUSTOM_PORT, - isValidInput = true, - showResetToDefault = true, - onResetPort = mockedClickHandler, - ) - } + initDialog( + portInput = VALID_CUSTOM_PORT, + isValidInput = true, + showResetToDefault = true, + onResetPort = mockedClickHandler, + ) // Act onNodeWithText("Remove custom port").performClick() @@ -163,7 +164,7 @@ class CustomPortDialogTest { composeExtension.use { // Arrange val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { testWireguardCustomPortDialog(onDismiss = mockedClickHandler) } + initDialog(onDismiss = mockedClickHandler) // Assert onNodeWithText("Cancel").performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialogTest.kt index abe7abfbcfb6..6749e620a599 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialogTest.kt @@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.compose.dialog import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -25,16 +26,23 @@ class DeleteCustomListConfirmationDialogTest { MockKAnnotations.init(this) } + private fun ComposeContext.initDialog( + state: DeleteCustomListUiState = + DeleteCustomListUiState(CustomListName.fromString("My Custom List"), null), + onDelete: () -> Unit = {}, + onBack: () -> Unit = {}, + ) { + setContentWithTheme { + DeleteCustomListConfirmationDialog(state = state, onDelete = onDelete, onBack = onBack) + } + } + @Test fun givenNameShouldShowDeleteNameTitle() = composeExtension.use { // Arrange val name = CustomListName.fromString("List should be deleted") - setContentWithTheme { - DeleteCustomListConfirmationDialog( - state = DeleteCustomListUiState(name = name, deleteError = null) - ) - } + initDialog(state = DeleteCustomListUiState(name = name, deleteError = null)) // Assert onNodeWithText(DELETE_TITLE.format(name)).assertExists() @@ -46,12 +54,10 @@ class DeleteCustomListConfirmationDialogTest { // Arrange val name = CustomListName.fromString("List should be deleted") val mockedOnDelete: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - DeleteCustomListConfirmationDialog( - state = DeleteCustomListUiState(name = name, deleteError = null), - onDelete = mockedOnDelete, - ) - } + initDialog( + state = DeleteCustomListUiState(name = name, deleteError = null), + onDelete = mockedOnDelete, + ) // Act onNodeWithText(DELETE_BUTTON_TEXT).performClick() @@ -66,12 +72,10 @@ class DeleteCustomListConfirmationDialogTest { // Arrange val name = CustomListName.fromString("List should be deleted") val mockedOnBack: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - DeleteCustomListConfirmationDialog( - state = DeleteCustomListUiState(name = name, deleteError = null), - onBack = mockedOnBack, - ) - } + initDialog( + state = DeleteCustomListUiState(name = name, deleteError = null), + onBack = mockedOnBack, + ) // Act onNodeWithText(CANCEL_BUTTON_TEXT).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt index faafce1137a6..56bdc562fdea 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt @@ -1,10 +1,9 @@ package net.mullvad.mullvadvpn.compose.dialog -import android.annotation.SuppressLint -import androidx.compose.runtime.Composable import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsNotEnabled import androidx.compose.ui.test.onNodeWithText +import de.mannodermaus.junit5.compose.ComposeContext import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.viewmodel.DnsDialogViewState @@ -27,25 +26,29 @@ class DnsDialogTest { index = null, ) - @SuppressLint("ComposableNaming") - @Composable - private fun testDnsDialog( + private fun ComposeContext.initDialog( state: DnsDialogViewState = defaultState, onDnsInputChange: (String) -> Unit = { _ -> }, onSaveDnsClick: () -> Unit = {}, onRemoveDnsClick: (Int) -> Unit = {}, onDismiss: () -> Unit = {}, ) { - DnsDialog(state, onDnsInputChange, onSaveDnsClick, onRemoveDnsClick, onDismiss) + setContentWithTheme { + DnsDialog( + state = state, + onDnsInputChange = onDnsInputChange, + onSaveDnsClick = onSaveDnsClick, + onRemoveDnsClick = onRemoveDnsClick, + onDismiss = onDismiss, + ) + } } @Test fun testDnsDialogLanWarningShownWhenLanTrafficDisabledAndLocalAddressUsed() = composeExtension.use { // Arrange - setContentWithTheme { - testDnsDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = true)) - } + initDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = true)) // Assert onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertExists() @@ -55,9 +58,7 @@ class DnsDialogTest { fun testDnsDialogLanWarningNotShownWhenLanTrafficEnabledAndLocalAddressUsed() = composeExtension.use { // Arrange - setContentWithTheme { - testDnsDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = true)) - } + initDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = true)) // Assert onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() @@ -67,9 +68,7 @@ class DnsDialogTest { fun testDnsDialogLanWarningNotShownWhenLanTrafficEnabledAndNonLocalAddressUsed() = composeExtension.use { // Arrange - setContentWithTheme { - testDnsDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = false)) - } + initDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = false)) // Assert onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() @@ -79,9 +78,7 @@ class DnsDialogTest { fun testDnsDialogLanWarningNotShownWhenLanTrafficDisabledAndNonLocalAddressUsed() = composeExtension.use { // Arrange - setContentWithTheme { - testDnsDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = false)) - } + initDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = false)) // Assert onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() @@ -91,14 +88,12 @@ class DnsDialogTest { fun testDnsDialogSubmitButtonDisabledOnInvalidDnsAddress() = composeExtension.use { // Arrange - setContentWithTheme { - testDnsDialog( - defaultState.copy( - input = invalidIpAddress, - validationError = ValidationError.InvalidAddress, - ) + initDialog( + defaultState.copy( + input = invalidIpAddress, + validationError = ValidationError.InvalidAddress, ) - } + ) // Assert onNodeWithText("Submit").assertIsNotEnabled() @@ -108,14 +103,12 @@ class DnsDialogTest { fun testDnsDialogSubmitButtonDisabledOnDuplicateDnsAddress() = composeExtension.use { // Arrange - setContentWithTheme { - testDnsDialog( - defaultState.copy( - input = "192.168.0.1", - validationError = ValidationError.DuplicateAddress, - ) + initDialog( + defaultState.copy( + input = "192.168.0.1", + validationError = ValidationError.DuplicateAddress, ) - } + ) // Assert onNodeWithText("Submit").assertIsNotEnabled() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialogTest.kt index d43a86203746..a939b72ccfa0 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialogTest.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -31,12 +32,28 @@ class EditCustomListNameDialogTest { MockKAnnotations.init(this) } + private fun ComposeContext.initDialog( + state: EditCustomListNameUiState = EditCustomListNameUiState(), + updateName: (String) -> Unit = {}, + onInputChanged: (String) -> Unit = {}, + onDismiss: () -> Unit = {}, + ) { + setContentWithTheme { + EditCustomListNameDialog( + state = state, + updateName = updateName, + onInputChanged = onInputChanged, + onDismiss = onDismiss, + ) + } + } + @Test fun givenNoErrorShouldShowNoErrorMessage() = composeExtension.use { // Arrange val state = EditCustomListNameUiState(error = null) - setContentWithTheme { EditCustomListNameDialog(state = state) } + initDialog(state = state) // Assert onNodeWithText(NAME_EXIST_ERROR_TEXT).assertDoesNotExist() @@ -51,7 +68,7 @@ class EditCustomListNameDialogTest { EditCustomListNameUiState( error = RenameError(NameAlreadyExists(CustomListName.fromString("name"))) ) - setContentWithTheme { EditCustomListNameDialog(state = state) } + initDialog(state = state) // Assert onNodeWithText(NAME_EXIST_ERROR_TEXT).assertExists() @@ -66,7 +83,7 @@ class EditCustomListNameDialogTest { EditCustomListNameUiState( error = RenameError(UnknownCustomListError(RuntimeException(""))) ) - setContentWithTheme { EditCustomListNameDialog(state = state) } + initDialog(state = state) // Assert onNodeWithText(NAME_EXIST_ERROR_TEXT).assertDoesNotExist() @@ -79,9 +96,7 @@ class EditCustomListNameDialogTest { // Arrange val mockedOnDismiss: () -> Unit = mockk(relaxed = true) val state = EditCustomListNameUiState() - setContentWithTheme { - EditCustomListNameDialog(state = state, onDismiss = mockedOnDismiss) - } + initDialog(state = state, onDismiss = mockedOnDismiss) // Act onNodeWithText(CANCEL_BUTTON_TEXT).performClick() @@ -96,9 +111,7 @@ class EditCustomListNameDialogTest { // Arrange val mockedUpdateName: (String) -> Unit = mockk(relaxed = true) val state = EditCustomListNameUiState() - setContentWithTheme { - EditCustomListNameDialog(state = state, updateName = mockedUpdateName) - } + initDialog(state = state, updateName = mockedUpdateName) // Act onNodeWithText(SAVE_BUTTON_TEXT).performClick() @@ -114,9 +127,7 @@ class EditCustomListNameDialogTest { val mockedUpdateName: (String) -> Unit = mockk(relaxed = true) val inputText = "NEW NAME" val state = EditCustomListNameUiState(name = inputText) - setContentWithTheme { - EditCustomListNameDialog(state = state, updateName = mockedUpdateName) - } + initDialog(state, updateName = mockedUpdateName) // Act onNodeWithText(SAVE_BUTTON_TEXT).performClick() @@ -132,9 +143,7 @@ class EditCustomListNameDialogTest { val mockedOnInputChanged: (String) -> Unit = mockk(relaxed = true) val inputText = "NEW NAME" val state = EditCustomListNameUiState() - setContentWithTheme { - EditCustomListNameDialog(state = state, onInputChanged = mockedOnInputChanged) - } + initDialog(state, onInputChanged = mockedOnInputChanged) // Act onNodeWithTag(EDIT_CUSTOM_LIST_DIALOG_INPUT_TEST_TAG).performTextInput(inputText) diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialogTest.kt index 9d6b09fe2d7b..052c7846d974 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialogTest.kt @@ -1,12 +1,11 @@ package net.mullvad.mullvadvpn.compose.dialog -import android.annotation.SuppressLint -import androidx.compose.runtime.Composable import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsEnabled import androidx.compose.ui.test.assertIsNotEnabled import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -28,31 +27,32 @@ class MtuDialogTest { MockKAnnotations.init(this) } - @SuppressLint("ComposableNaming") - @Composable - private fun testMtuDialog( - mtuInput: String = "", - isValidInput: Boolean = true, - showResetButton: Boolean = true, - onInputChanged: (String) -> Unit = { _ -> }, - onSaveMtu: (String) -> Unit = { _ -> }, + private val defaultState = + MtuDialogUiState(mtuInput = "", isValidInput = true, showResetToDefault = true) + + private fun ComposeContext.initDialog( + state: MtuDialogUiState = defaultState, + onInputChanged: (String) -> Unit = {}, + onSaveMtu: (String) -> Unit = {}, onResetMtu: () -> Unit = {}, onDismiss: () -> Unit = {}, ) { - MtuDialog( - MtuDialogUiState(mtuInput, isValidInput, showResetButton), - onInputChanged = onInputChanged, - onSaveMtu = onSaveMtu, - onResetMtu = onResetMtu, - onDismiss = onDismiss, - ) + setContentWithTheme { + MtuDialog( + state = state, + onInputChanged = onInputChanged, + onSaveMtu = onSaveMtu, + onResetMtu = onResetMtu, + onDismiss = onDismiss, + ) + } } @Test fun testMtuDialogWithDefaultValue() = composeExtension.use { // Arrange - setContentWithTheme { testMtuDialog() } + initDialog() // Assert onNodeWithText(EMPTY_STRING).assertExists() @@ -62,7 +62,7 @@ class MtuDialogTest { fun testMtuDialogWithEditValue() = composeExtension.use { // Arrange - setContentWithTheme { testMtuDialog(mtuInput = VALID_DUMMY_MTU_VALUE) } + initDialog(defaultState.copy(mtuInput = VALID_DUMMY_MTU_VALUE)) // Assert onNodeWithText(VALID_DUMMY_MTU_VALUE).assertExists() @@ -73,9 +73,10 @@ class MtuDialogTest { composeExtension.use { // Arrange val mockedSubmitHandler: (String) -> Unit = mockk(relaxed = true) - setContentWithTheme { - testMtuDialog(VALID_DUMMY_MTU_VALUE, onSaveMtu = mockedSubmitHandler) - } + initDialog( + defaultState.copy(mtuInput = VALID_DUMMY_MTU_VALUE), + onSaveMtu = mockedSubmitHandler, + ) // Act onNodeWithText("Submit").assertIsEnabled().performClick() @@ -88,7 +89,7 @@ class MtuDialogTest { fun testMtuDialogSubmitButtonDisabledWhenInvalidInput() = composeExtension.use { // Arrange - setContentWithTheme { testMtuDialog(INVALID_DUMMY_MTU_VALUE, false) } + initDialog(defaultState.copy(mtuInput = INVALID_DUMMY_MTU_VALUE, isValidInput = false)) // Assert onNodeWithText("Submit").assertIsNotEnabled() @@ -99,9 +100,10 @@ class MtuDialogTest { composeExtension.use { // Arrange val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - testMtuDialog(mtuInput = VALID_DUMMY_MTU_VALUE, onResetMtu = mockedClickHandler) - } + initDialog( + defaultState.copy(mtuInput = VALID_DUMMY_MTU_VALUE), + onResetMtu = mockedClickHandler, + ) // Act onNodeWithText("Reset to default").performClick() @@ -115,7 +117,7 @@ class MtuDialogTest { composeExtension.use { // Arrange val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { testMtuDialog(onDismiss = mockedClickHandler) } + initDialog(onDismiss = mockedClickHandler) // Assert onNodeWithText("Cancel").performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/PaymentDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/PaymentDialogTest.kt index 3a77a5620a2b..09a5e9dd7286 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/PaymentDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/PaymentDialogTest.kt @@ -2,8 +2,10 @@ package net.mullvad.mullvadvpn.compose.dialog import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText +import de.mannodermaus.junit5.compose.ComposeContext import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension import net.mullvad.mullvadvpn.compose.dialog.payment.PaymentDialog +import net.mullvad.mullvadvpn.compose.dialog.payment.PaymentDialogData import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.lib.payment.model.ProductId import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult @@ -17,15 +19,25 @@ class PaymentDialogTest { @RegisterExtension val composeExtension = createEdgeToEdgeComposeExtension() + private fun ComposeContext.initDialog( + paymentDialogData: PaymentDialogData, + retryPurchase: (ProductId) -> Unit = {}, + onCloseDialog: (isPaymentSuccessful: Boolean) -> Unit = {}, + ) { + setContentWithTheme { + PaymentDialog( + paymentDialogData = paymentDialogData, + retryPurchase = retryPurchase, + onCloseDialog = onCloseDialog, + ) + } + } + @Test fun testShowPurchaseCompleteDialog() = composeExtension.use { // Arrange - setContentWithTheme { - PaymentDialog( - paymentDialogData = PurchaseResult.Completed.Success.toPaymentDialogData()!! - ) - } + initDialog(paymentDialogData = PurchaseResult.Completed.Success.toPaymentDialogData()!!) // Assert onNodeWithText("Time was successfully added").assertExists() @@ -35,12 +47,10 @@ class PaymentDialogTest { fun testShowVerificationErrorDialog() = composeExtension.use { // Arrange - setContentWithTheme { - PaymentDialog( - paymentDialogData = - PurchaseResult.Error.VerificationError(null).toPaymentDialogData()!! - ) - } + initDialog( + paymentDialogData = + PurchaseResult.Error.VerificationError(null).toPaymentDialogData()!! + ) // Assert onNodeWithText("Verifying purchase").assertExists() @@ -50,13 +60,11 @@ class PaymentDialogTest { fun testShowFetchProductsErrorDialog() = composeExtension.use { // Arrange - setContentWithTheme { - PaymentDialog( - paymentDialogData = - PurchaseResult.Error.FetchProductsError(ProductId(""), null) - .toPaymentDialogData()!! - ) - } + initDialog( + paymentDialogData = + PurchaseResult.Error.FetchProductsError(ProductId(""), null) + .toPaymentDialogData()!! + ) // Assert onNodeWithText("Google Play unavailable").assertExists() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/RedeemVoucherDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialogTest.kt similarity index 59% rename from android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/RedeemVoucherDialogTest.kt rename to android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialogTest.kt index 10e7af7923e9..b87b43e3c68f 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/RedeemVoucherDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialogTest.kt @@ -1,15 +1,15 @@ -package net.mullvad.mullvadvpn.compose.screen +package net.mullvad.mullvadvpn.compose.dialog import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.mockk import io.mockk.mockkObject import io.mockk.verify import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension -import net.mullvad.mullvadvpn.compose.dialog.RedeemVoucherDialog import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.compose.state.VoucherDialogState import net.mullvad.mullvadvpn.compose.state.VoucherDialogUiState @@ -29,19 +29,28 @@ class RedeemVoucherDialogTest { mockkObject(VoucherRegexHelper) } + private fun ComposeContext.initDialog( + state: VoucherDialogUiState = VoucherDialogUiState.INITIAL, + onVoucherInputChange: (String) -> Unit = {}, + onRedeem: (voucherCode: String) -> Unit = {}, + onDismiss: (isTimeAdded: Boolean) -> Unit = {}, + ) { + setContentWithTheme { + RedeemVoucherDialog( + state = state, + onVoucherInputChange = onVoucherInputChange, + onRedeem = onRedeem, + onDismiss = onDismiss, + ) + } + } + @Test fun testDismissDialog() = composeExtension.use { // Arrange val mockedClickHandler: (Boolean) -> Unit = mockk(relaxed = true) - setContentWithTheme { - RedeemVoucherDialog( - state = VoucherDialogUiState.INITIAL, - onVoucherInputChange = {}, - onRedeem = {}, - onDismiss = mockedClickHandler, - ) - } + initDialog(onDismiss = mockedClickHandler) // Act onNodeWithText(CANCEL_BUTTON_TEXT).performClick() @@ -55,14 +64,10 @@ class RedeemVoucherDialogTest { composeExtension.use { // Arrange val mockedClickHandler: (Boolean) -> Unit = mockk(relaxed = true) - setContentWithTheme { - RedeemVoucherDialog( - state = VoucherDialogUiState(voucherState = VoucherDialogState.Success(0)), - onVoucherInputChange = {}, - onRedeem = {}, - onDismiss = mockedClickHandler, - ) - } + initDialog( + state = VoucherDialogUiState(voucherState = VoucherDialogState.Success(0)), + onDismiss = mockedClickHandler, + ) // Act onNodeWithText(GOT_IT_BUTTON_TEXT).performClick() @@ -76,14 +81,7 @@ class RedeemVoucherDialogTest { composeExtension.use { // Arrange val mockedClickHandler: (String) -> Unit = mockk(relaxed = true) - setContentWithTheme { - RedeemVoucherDialog( - state = VoucherDialogUiState(), - onVoucherInputChange = mockedClickHandler, - onRedeem = {}, - onDismiss = {}, - ) - } + initDialog(state = VoucherDialogUiState(), onVoucherInputChange = mockedClickHandler) // Act onNodeWithTag(VOUCHER_INPUT_TEST_TAG).performTextInput(DUMMY_VOUCHER) @@ -96,14 +94,7 @@ class RedeemVoucherDialogTest { fun testVerifyingState() = composeExtension.use { // Arrange - setContentWithTheme { - RedeemVoucherDialog( - state = VoucherDialogUiState(voucherState = VoucherDialogState.Verifying), - onVoucherInputChange = {}, - onRedeem = {}, - onDismiss = {}, - ) - } + initDialog(state = VoucherDialogUiState(voucherState = VoucherDialogState.Verifying)) // Assert onNodeWithText("Verifying voucher…").assertExists() @@ -113,14 +104,7 @@ class RedeemVoucherDialogTest { fun testSuccessState() = composeExtension.use { // Arrange - setContentWithTheme { - RedeemVoucherDialog( - state = VoucherDialogUiState(voucherState = VoucherDialogState.Success(0)), - onVoucherInputChange = {}, - onRedeem = {}, - onDismiss = {}, - ) - } + initDialog(state = VoucherDialogUiState(voucherState = VoucherDialogState.Success(0))) // Assert onNodeWithText("Voucher was successfully redeemed.").assertExists() @@ -130,18 +114,12 @@ class RedeemVoucherDialogTest { fun testErrorState() = composeExtension.use { // Arrange - setContentWithTheme { - RedeemVoucherDialog( - state = - VoucherDialogUiState( - voucherState = - VoucherDialogState.Error(RedeemVoucherError.InvalidVoucher) - ), - onVoucherInputChange = {}, - onRedeem = {}, - onDismiss = {}, - ) - } + initDialog( + state = + VoucherDialogUiState( + voucherState = VoucherDialogState.Error(RedeemVoucherError.InvalidVoucher) + ) + ) // Assert onNodeWithText(VOUCHER_CODE_INVALID_ERROR_MESSAGE).assertExists() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/ResetServerIPOverridesConfirmationDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/ResetServerIPOverridesConfirmationDialogTest.kt index 107c03f1d6ce..818cec27f3ff 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/ResetServerIPOverridesConfirmationDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/ResetServerIPOverridesConfirmationDialogTest.kt @@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.compose.dialog import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -25,18 +26,25 @@ class ResetServerIPOverridesConfirmationDialogTest { MockKAnnotations.init(this) } + private fun ComposeContext.initDialog( + onClearAllOverrides: () -> Unit = {}, + onNavigateBack: () -> Unit = {}, + ) { + setContentWithTheme { + ResetServerIpOverridesConfirmationDialog( + onClearAllOverrides = onClearAllOverrides, + onNavigateBack = onNavigateBack, + ) + } + } + @Test - fun ensure_cancel_click_works() = + fun ensureCancelClickWorks() = composeExtension.use { val clickHandler: () -> Unit = mockk(relaxed = true) // Arrange - setContentWithTheme { - ResetServerIpOverridesConfirmationDialog( - onNavigateBack = clickHandler, - onClearAllOverrides = {}, - ) - } + initDialog(onNavigateBack = clickHandler) // Act onNodeWithTag(RESET_SERVER_IP_OVERRIDE_CANCEL_TEST_TAG).performClick() @@ -46,17 +54,12 @@ class ResetServerIPOverridesConfirmationDialogTest { } @Test - fun ensure_reset_click_works() = + fun ensureResetClickWorks() = composeExtension.use { val clickHandler: () -> Unit = mockk(relaxed = true) // Arrange - setContentWithTheme { - ResetServerIpOverridesConfirmationDialog( - onNavigateBack = {}, - onClearAllOverrides = clickHandler, - ) - } + initDialog(onClearAllOverrides = clickHandler) // Act onNodeWithTag(RESET_SERVER_IP_OVERRIDE_RESET_TEST_TAG).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialogTest.kt index afd6d0e15d1a..a9ad169b0103 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialogTest.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsNotEnabled import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.mockk import io.mockk.verify import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension @@ -20,19 +21,27 @@ import org.junit.jupiter.api.extension.RegisterExtension class SaveApiAccessMethodDialogTest { @JvmField @RegisterExtension val composeExtension = createEdgeToEdgeComposeExtension() + private fun ComposeContext.initDialog( + state: SaveApiAccessMethodUiState = SaveApiAccessMethodUiState(), + onCancel: () -> Unit = {}, + onSave: () -> Unit = {}, + ) { + setContentWithTheme { + SaveApiAccessMethodDialog(state = state, onCancel = onCancel, onSave = onSave) + } + } + @Test fun whenTestingInProgressShouldShowSpinnerWithCancelButton() = composeExtension.use { // Arrange - setContentWithTheme { - SaveApiAccessMethodDialog( - state = - SaveApiAccessMethodUiState( - testingState = TestApiAccessMethodState.Testing, - isSaving = false, - ) - ) - } + initDialog( + state = + SaveApiAccessMethodUiState( + testingState = TestApiAccessMethodState.Testing, + isSaving = false, + ) + ) // Assert onNodeWithTag(SAVE_API_ACCESS_METHOD_LOADING_SPINNER_TEST_TAG).assertExists() @@ -43,15 +52,13 @@ class SaveApiAccessMethodDialogTest { fun whenTestingFailedShouldShowSaveAndCancelButton() = composeExtension.use { // Arrange - setContentWithTheme { - SaveApiAccessMethodDialog( - state = - SaveApiAccessMethodUiState( - testingState = TestApiAccessMethodState.Result.Failure, - isSaving = false, - ) - ) - } + initDialog( + state = + SaveApiAccessMethodUiState( + testingState = TestApiAccessMethodState.Result.Failure, + isSaving = false, + ) + ) // Assert onNodeWithTag(SAVE_API_ACCESS_METHOD_SAVE_BUTTON_TEST_TAG).assertExists() @@ -62,15 +69,13 @@ class SaveApiAccessMethodDialogTest { fun whenTestingSuccessfulAndSavingShouldShowDisabledCancelButton() = composeExtension.use { // Arrange - setContentWithTheme { - SaveApiAccessMethodDialog( - state = - SaveApiAccessMethodUiState( - testingState = TestApiAccessMethodState.Result.Successful, - isSaving = true, - ) - ) - } + initDialog( + state = + SaveApiAccessMethodUiState( + testingState = TestApiAccessMethodState.Result.Successful, + isSaving = true, + ) + ) // Assert onNodeWithTag(SAVE_API_ACCESS_METHOD_CANCEL_BUTTON_TEST_TAG).assertExists() @@ -82,16 +87,14 @@ class SaveApiAccessMethodDialogTest { composeExtension.use { // Arrange val onCancelClick: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - SaveApiAccessMethodDialog( - state = - SaveApiAccessMethodUiState( - testingState = TestApiAccessMethodState.Testing, - isSaving = false, - ), - onCancel = onCancelClick, - ) - } + initDialog( + state = + SaveApiAccessMethodUiState( + testingState = TestApiAccessMethodState.Testing, + isSaving = false, + ), + onCancel = onCancelClick, + ) // Act onNodeWithTag(SAVE_API_ACCESS_METHOD_CANCEL_BUTTON_TEST_TAG).performClick() @@ -105,16 +108,14 @@ class SaveApiAccessMethodDialogTest { composeExtension.use { // Arrange val onSaveClick: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - SaveApiAccessMethodDialog( - state = - SaveApiAccessMethodUiState( - testingState = TestApiAccessMethodState.Result.Failure, - isSaving = false, - ), - onSave = onSaveClick, - ) - } + initDialog( + state = + SaveApiAccessMethodUiState( + testingState = TestApiAccessMethodState.Result.Failure, + isSaving = false, + ), + onSave = onSaveClick, + ) // Act onNodeWithTag(SAVE_API_ACCESS_METHOD_SAVE_BUTTON_TEST_TAG).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt index dba40b40ddce..88b783df8334 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreenTest.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.mockk @@ -33,23 +34,47 @@ class AccountScreenTest { MockKAnnotations.init(this) } + private fun ComposeContext.initScreen( + state: AccountUiState = AccountUiState.default(), + onCopyAccountNumber: (String) -> Unit = {}, + onRedeemVoucherClick: () -> Unit = {}, + onManageAccountClick: () -> Unit = {}, + onLogoutClick: () -> Unit = {}, + onPurchaseBillingProductClick: (productId: ProductId) -> Unit = {}, + navigateToDeviceInfo: () -> Unit = {}, + navigateToVerificationPendingDialog: () -> Unit = {}, + onBackClick: () -> Unit = {}, + ) { + setContentWithTheme { + AccountScreen( + state = state, + onCopyAccountNumber = onCopyAccountNumber, + onRedeemVoucherClick = onRedeemVoucherClick, + onManageAccountClick = onManageAccountClick, + onLogoutClick = onLogoutClick, + onPurchaseBillingProductClick = onPurchaseBillingProductClick, + navigateToDeviceInfo = navigateToDeviceInfo, + navigateToVerificationPendingDialog = navigateToVerificationPendingDialog, + onBackClick = onBackClick, + ) + } + } + @Test fun testDefaultState() = composeExtension.use { // Arrange - setContentWithTheme { - AccountScreen( - state = - AccountUiState( - deviceName = DUMMY_DEVICE_NAME, - accountNumber = DUMMY_ACCOUNT_NUMBER, - accountExpiry = null, - showSitePayment = false, - showLogoutLoading = false, - showManageAccountLoading = false, - ) - ) - } + initScreen( + state = + AccountUiState( + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null, + showSitePayment = false, + showLogoutLoading = false, + showManageAccountLoading = false, + ) + ) // Assert onNodeWithText("Redeem voucher").assertExists() @@ -61,20 +86,18 @@ class AccountScreenTest { composeExtension.use { // Arrange val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - AccountScreen( - state = - AccountUiState( - deviceName = DUMMY_DEVICE_NAME, - accountNumber = DUMMY_ACCOUNT_NUMBER, - accountExpiry = null, - showSitePayment = true, - showLogoutLoading = false, - showManageAccountLoading = false, - ), - onManageAccountClick = mockedClickHandler, - ) - } + initScreen( + state = + AccountUiState( + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null, + showSitePayment = true, + showLogoutLoading = false, + showManageAccountLoading = false, + ), + onManageAccountClick = mockedClickHandler, + ) // Act onNodeWithText("Manage account").performClick() @@ -88,20 +111,18 @@ class AccountScreenTest { composeExtension.use { // Arrange val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - AccountScreen( - state = - AccountUiState( - deviceName = DUMMY_DEVICE_NAME, - accountNumber = DUMMY_ACCOUNT_NUMBER, - accountExpiry = null, - showSitePayment = false, - showLogoutLoading = false, - showManageAccountLoading = false, - ), - onRedeemVoucherClick = mockedClickHandler, - ) - } + initScreen( + state = + AccountUiState( + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null, + showSitePayment = false, + showLogoutLoading = false, + showManageAccountLoading = false, + ), + onRedeemVoucherClick = mockedClickHandler, + ) // Act onNodeWithText("Redeem voucher").performClick() @@ -115,20 +136,18 @@ class AccountScreenTest { composeExtension.use { // Arrange val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - AccountScreen( - state = - AccountUiState( - deviceName = DUMMY_DEVICE_NAME, - accountNumber = DUMMY_ACCOUNT_NUMBER, - accountExpiry = null, - showSitePayment = false, - showLogoutLoading = false, - showManageAccountLoading = false, - ), - onLogoutClick = mockedClickHandler, - ) - } + initScreen( + state = + AccountUiState( + deviceName = DUMMY_DEVICE_NAME, + accountNumber = DUMMY_ACCOUNT_NUMBER, + accountExpiry = null, + showSitePayment = false, + showLogoutLoading = false, + showManageAccountLoading = false, + ), + onLogoutClick = mockedClickHandler, + ) // Act onNodeWithText("Log out").performClick() @@ -141,13 +160,10 @@ class AccountScreenTest { fun testShowBillingErrorPaymentButton() = composeExtension.use { // Arrange - setContentWithTheme { - AccountScreen( - state = - AccountUiState.default() - .copy(billingPaymentState = PaymentState.Error.Billing) - ) - } + initScreen( + state = + AccountUiState.default().copy(billingPaymentState = PaymentState.Error.Billing) + ) // Assert onNodeWithText("Add 30 days time").assertExists() @@ -160,16 +176,14 @@ class AccountScreenTest { val mockPaymentProduct: PaymentProduct = mockk() every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns null - setContentWithTheme { - AccountScreen( - state = - AccountUiState.default() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ) - ) - } + initScreen( + state = + AccountUiState.default() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ) + ) // Assert onNodeWithText("Add 30 days time ($10)").assertExists() @@ -182,16 +196,14 @@ class AccountScreenTest { val mockPaymentProduct: PaymentProduct = mockk() every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns PaymentStatus.PENDING - setContentWithTheme { - AccountScreen( - state = - AccountUiState.default() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ) - ) - } + initScreen( + state = + AccountUiState.default() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ) + ) // Assert onNodeWithText("Google Play payment pending").assertExists() @@ -205,17 +217,15 @@ class AccountScreenTest { every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns PaymentStatus.PENDING val mockNavigateToVerificationPending: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - AccountScreen( - state = - AccountUiState.default() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - navigateToVerificationPendingDialog = mockNavigateToVerificationPending, - ) - } + initScreen( + state = + AccountUiState.default() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + navigateToVerificationPendingDialog = mockNavigateToVerificationPending, + ) // Act onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick() @@ -231,16 +241,14 @@ class AccountScreenTest { val mockPaymentProduct: PaymentProduct = mockk() every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns PaymentStatus.VERIFICATION_IN_PROGRESS - setContentWithTheme { - AccountScreen( - state = - AccountUiState.default() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ) - ) - } + initScreen( + state = + AccountUiState.default() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ) + ) // Assert onNodeWithText("Verifying purchase").assertExists() @@ -255,17 +263,15 @@ class AccountScreenTest { every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.productId } returns ProductId("PRODUCT_ID") every { mockPaymentProduct.status } returns null - setContentWithTheme { - AccountScreen( - state = - AccountUiState.default() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - onPurchaseBillingProductClick = clickHandler, - ) - } + initScreen( + state = + AccountUiState.default() + .copy( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + onPurchaseBillingProductClick = clickHandler, + ) // Act onNodeWithText("Add 30 days time ($10)").performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessListScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessListScreenTest.kt index e08d46d9d3e7..e0f24bcc5ee2 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessListScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessListScreenTest.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.mockk import io.mockk.verify import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension @@ -19,17 +20,32 @@ import org.junit.jupiter.api.extension.RegisterExtension class ApiAccessListScreenTest { @JvmField @RegisterExtension val composeExtension = createEdgeToEdgeComposeExtension() + private fun ComposeContext.initScreen( + state: ApiAccessListUiState = ApiAccessListUiState(), + onAddMethodClick: () -> Unit = {}, + onApiAccessMethodClick: (apiAccessMethodSetting: ApiAccessMethodSetting) -> Unit = {}, + onApiAccessInfoClick: () -> Unit = {}, + onBackClick: () -> Unit = {}, + ) { + setContentWithTheme { + ApiAccessListScreen( + state = state, + onAddMethodClick = onAddMethodClick, + onApiAccessMethodClick = onApiAccessMethodClick, + onApiAccessInfoClick = onApiAccessInfoClick, + onBackClick = onBackClick, + ) + } + } + @Test fun shouldShowCurrentApiAccessName() = composeExtension.use { // Arrange val currentApiAccessMethod = DIRECT_ACCESS_METHOD - setContentWithTheme { - ApiAccessListScreen( - state = - ApiAccessListUiState(currentApiAccessMethodSetting = currentApiAccessMethod) - ) - } + initScreen( + state = ApiAccessListUiState(currentApiAccessMethodSetting = currentApiAccessMethod) + ) // Assert onNodeWithText("Current: ${currentApiAccessMethod.name}") @@ -40,11 +56,9 @@ class ApiAccessListScreenTest { composeExtension.use { // Arrange val apiAccessMethod = DIRECT_ACCESS_METHOD - setContentWithTheme { - ApiAccessListScreen( - state = ApiAccessListUiState(apiAccessMethodSettings = listOf(apiAccessMethod)) - ) - } + initScreen( + state = ApiAccessListUiState(apiAccessMethodSettings = listOf(apiAccessMethod)) + ) // Assert onNodeWithText(apiAccessMethod.name.value) @@ -56,12 +70,7 @@ class ApiAccessListScreenTest { composeExtension.use { // Arrange val onAddMethodClick: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ApiAccessListScreen( - state = ApiAccessListUiState(), - onAddMethodClick = onAddMethodClick, - ) - } + initScreen(state = ApiAccessListUiState(), onAddMethodClick = onAddMethodClick) // Act onNodeWithText("Add").performClick() @@ -75,12 +84,7 @@ class ApiAccessListScreenTest { composeExtension.use { // Arrange val onApiAccessInfoClick: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ApiAccessListScreen( - state = ApiAccessListUiState(), - onApiAccessInfoClick = onApiAccessInfoClick, - ) - } + initScreen(state = ApiAccessListUiState(), onApiAccessInfoClick = onApiAccessInfoClick) // Act onNodeWithTag(API_ACCESS_LIST_INFO_TEST_TAG).performClick() @@ -95,12 +99,10 @@ class ApiAccessListScreenTest { // Arrange val apiAccessMethod = DIRECT_ACCESS_METHOD val onApiAccessMethodClick: (ApiAccessMethodSetting) -> Unit = mockk(relaxed = true) - setContentWithTheme { - ApiAccessListScreen( - state = ApiAccessListUiState(apiAccessMethodSettings = listOf(apiAccessMethod)), - onApiAccessMethodClick = onApiAccessMethodClick, - ) - } + initScreen( + state = ApiAccessListUiState(apiAccessMethodSettings = listOf(apiAccessMethod)), + onApiAccessMethodClick = onApiAccessMethodClick, + ) // Act onNodeWithText(apiAccessMethod.name.value).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreenTest.kt index 93f3fb0343ce..14dfdfbfd7f9 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreenTest.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.mockk import io.mockk.verify import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension @@ -23,22 +24,44 @@ import org.junit.jupiter.api.extension.RegisterExtension class ApiAccessMethodDetailsScreenTest { @JvmField @RegisterExtension val composeExtension = createEdgeToEdgeComposeExtension() + private fun ComposeContext.initScreen( + state: ApiAccessMethodDetailsUiState, + onEditMethodClicked: () -> Unit = {}, + onEnableClicked: (Boolean) -> Unit = {}, + onTestMethodClicked: () -> Unit = {}, + onUseMethodClicked: () -> Unit = {}, + onDeleteApiAccessMethodClicked: (ApiAccessMethodId) -> Unit = {}, + onNavigateToEncryptedDnsInfoDialog: () -> Unit = {}, + onBackClicked: () -> Unit = {}, + ) { + setContentWithTheme { + ApiAccessMethodDetailsScreen( + state = state, + onEditMethodClicked = onEditMethodClicked, + onEnableClicked = onEnableClicked, + onTestMethodClicked = onTestMethodClicked, + onUseMethodClicked = onUseMethodClicked, + onDeleteApiAccessMethodClicked = onDeleteApiAccessMethodClicked, + onNavigateToEncryptedDnsInfoDialog = onNavigateToEncryptedDnsInfoDialog, + onBackClicked = onBackClicked, + ) + } + } + @Test fun whenApiAccessMethodIsNotEditableShouldNotShowDeleteAndEdit() = composeExtension.use { // Arrange val apiAccessMethod = DIRECT_ACCESS_METHOD - setContentWithTheme { - ApiAccessMethodDetailsScreen( - state = - ApiAccessMethodDetailsUiState.Content( - apiAccessMethodSetting = apiAccessMethod, - isDisableable = true, - isCurrentMethod = true, - isTestingAccessMethod = false, - ) - ) - } + initScreen( + state = + ApiAccessMethodDetailsUiState.Content( + apiAccessMethodSetting = apiAccessMethod, + isDisableable = true, + isCurrentMethod = true, + isTestingAccessMethod = false, + ) + ) // Assert onNodeWithTag(API_ACCESS_DETAILS_TOP_BAR_DROPDOWN_BUTTON_TEST_TAG).assertDoesNotExist() @@ -51,18 +74,16 @@ class ApiAccessMethodDetailsScreenTest { // Arrange val onEnableClicked: (Boolean) -> Unit = mockk(relaxed = true) val apiAccessMethod = DIRECT_ACCESS_METHOD - setContentWithTheme { - ApiAccessMethodDetailsScreen( - state = - ApiAccessMethodDetailsUiState.Content( - apiAccessMethodSetting = apiAccessMethod, - isDisableable = false, - isCurrentMethod = true, - isTestingAccessMethod = false, - ), - onEnableClicked = onEnableClicked, - ) - } + initScreen( + state = + ApiAccessMethodDetailsUiState.Content( + apiAccessMethodSetting = apiAccessMethod, + isDisableable = false, + isCurrentMethod = true, + isTestingAccessMethod = false, + ), + onEnableClicked = onEnableClicked, + ) // Act onNodeWithText("Enable method").performClick() @@ -78,18 +99,16 @@ class ApiAccessMethodDetailsScreenTest { // Arrange val onDeleteApiAccessMethodClicked: (ApiAccessMethodId) -> Unit = mockk(relaxed = true) val apiAccessMethod = CUSTOM_ACCESS_METHOD - setContentWithTheme { - ApiAccessMethodDetailsScreen( - state = - ApiAccessMethodDetailsUiState.Content( - apiAccessMethodSetting = apiAccessMethod, - isDisableable = false, - isCurrentMethod = true, - isTestingAccessMethod = false, - ), - onDeleteApiAccessMethodClicked = onDeleteApiAccessMethodClicked, - ) - } + initScreen( + state = + ApiAccessMethodDetailsUiState.Content( + apiAccessMethodSetting = apiAccessMethod, + isDisableable = false, + isCurrentMethod = true, + isTestingAccessMethod = false, + ), + onDeleteApiAccessMethodClicked = onDeleteApiAccessMethodClicked, + ) // Act onNodeWithTag(API_ACCESS_DETAILS_TOP_BAR_DROPDOWN_BUTTON_TEST_TAG).performClick() @@ -105,18 +124,16 @@ class ApiAccessMethodDetailsScreenTest { // Arrange val onEditMethodClicked: () -> Unit = mockk(relaxed = true) val apiAccessMethod = CUSTOM_ACCESS_METHOD - setContentWithTheme { - ApiAccessMethodDetailsScreen( - state = - ApiAccessMethodDetailsUiState.Content( - apiAccessMethodSetting = apiAccessMethod, - isDisableable = false, - isCurrentMethod = true, - isTestingAccessMethod = false, - ), - onEditMethodClicked = onEditMethodClicked, - ) - } + initScreen( + state = + ApiAccessMethodDetailsUiState.Content( + apiAccessMethodSetting = apiAccessMethod, + isDisableable = false, + isCurrentMethod = true, + isTestingAccessMethod = false, + ), + onEditMethodClicked = onEditMethodClicked, + ) // Act onNodeWithTag(API_ACCESS_DETAILS_EDIT_BUTTON).performClick() @@ -131,18 +148,16 @@ class ApiAccessMethodDetailsScreenTest { // Arrange val onEnableClicked: (Boolean) -> Unit = mockk(relaxed = true) val apiAccessMethod = DIRECT_ACCESS_METHOD - setContentWithTheme { - ApiAccessMethodDetailsScreen( - state = - ApiAccessMethodDetailsUiState.Content( - apiAccessMethodSetting = apiAccessMethod, - isDisableable = true, - isCurrentMethod = true, - isTestingAccessMethod = false, - ), - onEnableClicked = onEnableClicked, - ) - } + initScreen( + state = + ApiAccessMethodDetailsUiState.Content( + apiAccessMethodSetting = apiAccessMethod, + isDisableable = true, + isCurrentMethod = true, + isTestingAccessMethod = false, + ), + onEnableClicked = onEnableClicked, + ) // Act onNodeWithText("Enable method").performClick() @@ -157,18 +172,16 @@ class ApiAccessMethodDetailsScreenTest { // Arrange val onTestMethodClicked: () -> Unit = mockk(relaxed = true) val apiAccessMethod = DIRECT_ACCESS_METHOD - setContentWithTheme { - ApiAccessMethodDetailsScreen( - state = - ApiAccessMethodDetailsUiState.Content( - apiAccessMethodSetting = apiAccessMethod, - isDisableable = true, - isCurrentMethod = true, - isTestingAccessMethod = false, - ), - onTestMethodClicked = onTestMethodClicked, - ) - } + initScreen( + state = + ApiAccessMethodDetailsUiState.Content( + apiAccessMethodSetting = apiAccessMethod, + isDisableable = true, + isCurrentMethod = true, + isTestingAccessMethod = false, + ), + onTestMethodClicked = onTestMethodClicked, + ) // Act onNodeWithTag(API_ACCESS_TEST_METHOD_BUTTON).performClick() @@ -183,18 +196,16 @@ class ApiAccessMethodDetailsScreenTest { // Arrange val onUseMethodClicked: () -> Unit = mockk(relaxed = true) val apiAccessMethod = DIRECT_ACCESS_METHOD - setContentWithTheme { - ApiAccessMethodDetailsScreen( - state = - ApiAccessMethodDetailsUiState.Content( - apiAccessMethodSetting = apiAccessMethod, - isDisableable = true, - isCurrentMethod = false, - isTestingAccessMethod = false, - ), - onUseMethodClicked = onUseMethodClicked, - ) - } + initScreen( + state = + ApiAccessMethodDetailsUiState.Content( + apiAccessMethodSetting = apiAccessMethod, + isDisableable = true, + isCurrentMethod = false, + isTestingAccessMethod = false, + ), + onUseMethodClicked = onUseMethodClicked, + ) // Act onNodeWithTag(API_ACCESS_USE_METHOD_BUTTON).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt index 9087fe2f6c48..be998a37289e 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.mockk @@ -52,11 +53,41 @@ class ConnectScreenTest { unmockkAll() } + private fun ComposeContext.initScreen( + state: ConnectUiState = ConnectUiState.INITIAL, + onDisconnectClick: () -> Unit = {}, + onReconnectClick: () -> Unit = {}, + onConnectClick: () -> Unit = {}, + onCancelClick: () -> Unit = {}, + onSwitchLocationClick: () -> Unit = {}, + onOpenAppListing: () -> Unit = {}, + onManageAccountClick: () -> Unit = {}, + onSettingsClick: () -> Unit = {}, + onAccountClick: () -> Unit = {}, + onDismissNewDeviceClick: () -> Unit = {}, + ) { + setContentWithTheme { + ConnectScreen( + state = state, + onDisconnectClick = onDisconnectClick, + onReconnectClick = onReconnectClick, + onConnectClick = onConnectClick, + onCancelClick = onCancelClick, + onSwitchLocationClick = onSwitchLocationClick, + onOpenAppListing = onOpenAppListing, + onManageAccountClick = onManageAccountClick, + onSettingsClick = onSettingsClick, + onAccountClick = onAccountClick, + onDismissNewDeviceClick = onDismissNewDeviceClick, + ) + } + } + @Test fun testDefaultState() { composeExtension.use { // Arrange - setContentWithTheme { ConnectScreen(state = ConnectUiState.INITIAL) } + initScreen() // Assert onNodeWithText("DISCONNECTED").assertExists() @@ -68,21 +99,19 @@ class ConnectScreenTest { fun testConnectingState() { composeExtension.use { // Arrange - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null, emptyList()), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.TunnelStateBlocked, - isPlayBuild = false, - ) - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = null, + tunnelState = TunnelState.Connecting(null, null, emptyList()), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.TunnelStateBlocked, + isPlayBuild = false, + ) + ) // Assert onNodeWithTag(CIRCULAR_PROGRESS_INDICATOR).assertExists() @@ -98,22 +127,19 @@ class ConnectScreenTest { composeExtension.use { // Arrange val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = null, - tunnelState = - TunnelState.Connected(mockTunnelEndpoint, null, emptyList()), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false, - ) - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = null, + tunnelState = TunnelState.Connected(mockTunnelEndpoint, null, emptyList()), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false, + ) + ) // Assert onNodeWithText("CONNECTED").assertExists() @@ -127,21 +153,19 @@ class ConnectScreenTest { composeExtension.use { // Arrange val mockLocationName = "Home" - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = mockLocationName, - tunnelState = TunnelState.Disconnecting(ActionAfterDisconnect.Nothing), - showLocation = true, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false, - ) - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = mockLocationName, + tunnelState = TunnelState.Disconnecting(ActionAfterDisconnect.Nothing), + showLocation = true, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false, + ) + ) // Assert onNodeWithText("DISCONNECTED").assertExists() @@ -155,21 +179,19 @@ class ConnectScreenTest { composeExtension.use { // Arrange val mockLocationName = "Home" - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = mockLocationName, - tunnelState = TunnelState.Disconnected(), - showLocation = true, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false, - ) - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = mockLocationName, + tunnelState = TunnelState.Disconnected(), + showLocation = true, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false, + ) + ) // Assert onNodeWithText("DISCONNECTED").assertExists() @@ -183,27 +205,23 @@ class ConnectScreenTest { composeExtension.use { // Arrange val mockLocationName = "Home" - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = mockLocationName, - tunnelState = - TunnelState.Error( - ErrorState(ErrorStateCause.StartTunnelError, true) - ), - showLocation = true, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = - InAppNotification.TunnelStateError( - ErrorState(ErrorStateCause.StartTunnelError, true) - ), - isPlayBuild = false, - ) - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = mockLocationName, + tunnelState = + TunnelState.Error(ErrorState(ErrorStateCause.StartTunnelError, true)), + showLocation = true, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = + InAppNotification.TunnelStateError( + ErrorState(ErrorStateCause.StartTunnelError, true) + ), + isPlayBuild = false, + ) + ) // Assert onNodeWithText("BLOCKED CONNECTION").assertExists() @@ -218,27 +236,23 @@ class ConnectScreenTest { composeExtension.use { // Arrange val mockLocationName = "Home" - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = mockLocationName, - tunnelState = - TunnelState.Error( - ErrorState(ErrorStateCause.StartTunnelError, false) - ), - showLocation = true, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = - InAppNotification.TunnelStateError( - ErrorState(ErrorStateCause.StartTunnelError, false) - ), - isPlayBuild = false, - ) - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = mockLocationName, + tunnelState = + TunnelState.Error(ErrorState(ErrorStateCause.StartTunnelError, false)), + showLocation = true, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = + InAppNotification.TunnelStateError( + ErrorState(ErrorStateCause.StartTunnelError, false) + ), + isPlayBuild = false, + ) + ) // Assert onNodeWithText("FAILED TO CONNECT").assertExists() @@ -253,22 +267,19 @@ class ConnectScreenTest { fun testReconnectingState() { composeExtension.use { // Arrange - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = null, - tunnelState = - TunnelState.Disconnecting(ActionAfterDisconnect.Reconnect), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.TunnelStateBlocked, - isPlayBuild = false, - ) - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = null, + tunnelState = TunnelState.Disconnecting(ActionAfterDisconnect.Reconnect), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.TunnelStateBlocked, + isPlayBuild = false, + ) + ) // Assert onNodeWithTag(CIRCULAR_PROGRESS_INDICATOR).assertExists() @@ -284,21 +295,19 @@ class ConnectScreenTest { composeExtension.use { // Arrange val mockLocationName = "Home" - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = mockLocationName, - tunnelState = TunnelState.Disconnecting(ActionAfterDisconnect.Block), - showLocation = true, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.TunnelStateBlocked, - isPlayBuild = false, - ) - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = mockLocationName, + tunnelState = TunnelState.Disconnecting(ActionAfterDisconnect.Block), + showLocation = true, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.TunnelStateBlocked, + isPlayBuild = false, + ) + ) // Assert onNodeWithText("CONNECTED").assertExists() @@ -314,22 +323,20 @@ class ConnectScreenTest { // Arrange val mockLocationName = "Home" val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = mockLocationName, - tunnelState = TunnelState.Disconnected(), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false, - ), - onSwitchLocationClick = mockedClickHandler, - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = mockLocationName, + tunnelState = TunnelState.Disconnected(), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false, + ), + onSwitchLocationClick = mockedClickHandler, + ) // Act onNodeWithTag(SELECT_LOCATION_BUTTON_TEST_TAG).performClick() @@ -345,23 +352,20 @@ class ConnectScreenTest { // Arrange val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = null, - tunnelState = - TunnelState.Connected(mockTunnelEndpoint, null, emptyList()), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false, - ), - onDisconnectClick = mockedClickHandler, - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = null, + tunnelState = TunnelState.Connected(mockTunnelEndpoint, null, emptyList()), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false, + ), + onDisconnectClick = mockedClickHandler, + ) // Act onNodeWithTag(CONNECT_BUTTON_TEST_TAG).performClick() @@ -377,23 +381,20 @@ class ConnectScreenTest { // Arrange val mockTunnelEndpoint: TunnelEndpoint = mockk(relaxed = true) val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = null, - tunnelState = - TunnelState.Connected(mockTunnelEndpoint, null, emptyList()), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false, - ), - onReconnectClick = mockedClickHandler, - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = null, + tunnelState = TunnelState.Connected(mockTunnelEndpoint, null, emptyList()), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false, + ), + onReconnectClick = mockedClickHandler, + ) // Act onNodeWithTag(RECONNECT_BUTTON_TEST_TAG).performClick() @@ -408,22 +409,20 @@ class ConnectScreenTest { composeExtension.use { // Arrange val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = null, - tunnelState = TunnelState.Disconnected(), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false, - ), - onConnectClick = mockedClickHandler, - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = null, + tunnelState = TunnelState.Disconnected(), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false, + ), + onConnectClick = mockedClickHandler, + ) // Act onNodeWithTag(CONNECT_BUTTON_TEST_TAG).performClick() @@ -438,22 +437,20 @@ class ConnectScreenTest { composeExtension.use { // Arrange val mockedClickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null, emptyList()), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false, - ), - onCancelClick = mockedClickHandler, - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = null, + tunnelState = TunnelState.Connecting(null, null, emptyList()), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false, + ), + onCancelClick = mockedClickHandler, + ) // Act onNodeWithTag(CONNECT_BUTTON_TEST_TAG).performClick() @@ -490,26 +487,20 @@ class ConnectScreenTest { val outIpv6 = "ipv6address" every { mockLocation.ipv6?.hostAddress } returns outIpv6 - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = mockLocation, - selectedRelayItemTitle = null, - tunnelState = - TunnelState.Connected( - mockTunnelEndpoint, - mockLocation, - emptyList(), - ), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = null, - isPlayBuild = false, - ) - ) - } + initScreen( + state = + ConnectUiState( + location = mockLocation, + selectedRelayItemTitle = null, + tunnelState = + TunnelState.Connected(mockTunnelEndpoint, mockLocation, emptyList()), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = null, + isPlayBuild = false, + ) + ) // Act onNodeWithTag(CONNECT_CARD_HEADER_TEST_TAG).performClick() @@ -532,21 +523,19 @@ class ConnectScreenTest { composeExtension.use { // Arrange val versionInfo = VersionInfo(currentVersion = "1.0", isSupported = false) - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null, emptyList()), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.UnsupportedVersion(versionInfo), - isPlayBuild = false, - ) - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = null, + tunnelState = TunnelState.Connecting(null, null, emptyList()), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.UnsupportedVersion(versionInfo), + isPlayBuild = false, + ) + ) // Assert onNodeWithText("UNSUPPORTED VERSION").assertExists() @@ -562,24 +551,20 @@ class ConnectScreenTest { composeExtension.use { // Arrange val expiryDate = DateTime(2020, 11, 11, 10, 10) - setContentWithTheme { - ConnectScreen( - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null, emptyList()), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = - InAppNotification.AccountExpiry( - Duration(DateTime.now(), expiryDate) - ), - isPlayBuild = false, - ) - ) - } + initScreen( + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = null, + tunnelState = TunnelState.Connecting(null, null, emptyList()), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = + InAppNotification.AccountExpiry(Duration(DateTime.now(), expiryDate)), + isPlayBuild = false, + ) + ) // Assert onNodeWithText("ACCOUNT CREDIT EXPIRES SOON").assertExists() @@ -593,22 +578,20 @@ class ConnectScreenTest { // Arrange val mockedClickHandler: () -> Unit = mockk(relaxed = true) val versionInfo = VersionInfo(isSupported = false, currentVersion = "") - setContentWithTheme { - ConnectScreen( - onOpenAppListing = mockedClickHandler, - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null, emptyList()), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = InAppNotification.UnsupportedVersion(versionInfo), - isPlayBuild = false, - ), - ) - } + initScreen( + onOpenAppListing = mockedClickHandler, + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = null, + tunnelState = TunnelState.Connecting(null, null, emptyList()), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = InAppNotification.UnsupportedVersion(versionInfo), + isPlayBuild = false, + ), + ) // Act onNodeWithTag(NOTIFICATION_BANNER_ACTION).performClick() @@ -624,25 +607,21 @@ class ConnectScreenTest { // Arrange val mockedClickHandler: () -> Unit = mockk(relaxed = true) val expiryDate = DateTime(2020, 11, 11, 10, 10) - setContentWithTheme { - ConnectScreen( - onManageAccountClick = mockedClickHandler, - state = - ConnectUiState( - location = null, - selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null, emptyList()), - showLocation = false, - deviceName = "", - daysLeftUntilExpiry = null, - inAppNotification = - InAppNotification.AccountExpiry( - Duration(DateTime.now(), expiryDate) - ), - isPlayBuild = false, - ), - ) - } + initScreen( + onManageAccountClick = mockedClickHandler, + state = + ConnectUiState( + location = null, + selectedRelayItemTitle = null, + tunnelState = TunnelState.Connecting(null, null, emptyList()), + showLocation = false, + deviceName = "", + daysLeftUntilExpiry = null, + inAppNotification = + InAppNotification.AccountExpiry(Duration(DateTime.now(), expiryDate)), + isPlayBuild = false, + ), + ) // Act onNodeWithTag(NOTIFICATION_BANNER_ACTION).performClick() @@ -657,9 +636,7 @@ class ConnectScreenTest { composeExtension.use { // Arrange val onAccountClickMockk: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ConnectScreen(state = ConnectUiState.INITIAL, onAccountClick = onAccountClickMockk) - } + initScreen(state = ConnectUiState.INITIAL, onAccountClick = onAccountClickMockk) // Assert onNodeWithTag(TOP_BAR_ACCOUNT_BUTTON).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreenTest.kt index 484bb132d68f..dee081ba0317 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreenTest.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -29,15 +30,32 @@ class CustomListLocationsScreenTest { MockKAnnotations.init(this) } + private fun ComposeContext.initScreen( + state: CustomListLocationsUiState, + onSearchTermInput: (String) -> Unit = {}, + onSaveClick: () -> Unit = {}, + onRelaySelectionClick: (RelayItem.Location, selected: Boolean) -> Unit = { _, _ -> }, + onExpand: (RelayItem.Location, selected: Boolean) -> Unit = { _, _ -> }, + onBackClick: () -> Unit = {}, + ) { + + setContentWithTheme { + CustomListLocationsScreen( + state = state, + onSearchTermInput = onSearchTermInput, + onSaveClick = onSaveClick, + onRelaySelectionClick = onRelaySelectionClick, + onExpand = onExpand, + onBackClick = onBackClick, + ) + } + } + @Test fun givenLoadingStateShouldShowLoadingSpinner() = composeExtension.use { // Arrange - setContentWithTheme { - CustomListLocationsScreen( - state = CustomListLocationsUiState.Loading(newList = false) - ) - } + initScreen(state = CustomListLocationsUiState.Loading(newList = false)) // Assert onNodeWithTag(CIRCULAR_PROGRESS_INDICATOR).assertExists() @@ -48,11 +66,7 @@ class CustomListLocationsScreenTest { composeExtension.use { // Arrange val newList = true - setContentWithTheme { - CustomListLocationsScreen( - state = CustomListLocationsUiState.Loading(newList = newList) - ) - } + initScreen(state = CustomListLocationsUiState.Loading(newList = newList)) // Assert onNodeWithText(ADD_LOCATIONS_TEXT).assertExists() @@ -63,11 +77,7 @@ class CustomListLocationsScreenTest { composeExtension.use { // Arrange val newList = false - setContentWithTheme { - CustomListLocationsScreen( - state = CustomListLocationsUiState.Loading(newList = newList) - ) - } + initScreen(state = CustomListLocationsUiState.Loading(newList = newList)) // Assert onNodeWithText(EDIT_LOCATIONS_TEXT).assertExists() @@ -77,19 +87,17 @@ class CustomListLocationsScreenTest { fun givenListOfAvailableLocationsShouldShowThem() = composeExtension.use { // Arrange - setContentWithTheme { - CustomListLocationsScreen( - state = - CustomListLocationsUiState.Content.Data( - locations = - listOf( - RelayLocationListItem(DUMMY_RELAY_COUNTRIES[0], checked = true), - RelayLocationListItem(DUMMY_RELAY_COUNTRIES[1], checked = false), - ), - searchTerm = "", - ) - ) - } + initScreen( + state = + CustomListLocationsUiState.Content.Data( + locations = + listOf( + RelayLocationListItem(DUMMY_RELAY_COUNTRIES[0], checked = true), + RelayLocationListItem(DUMMY_RELAY_COUNTRIES[1], checked = false), + ), + searchTerm = "", + ) + ) // Assert onNodeWithText("Relay Country 1").assertExists() @@ -102,17 +110,14 @@ class CustomListLocationsScreenTest { // Arrange val selectedCountry = DUMMY_RELAY_COUNTRIES[0] val mockedOnRelaySelectionClicked: (RelayItem, Boolean) -> Unit = mockk(relaxed = true) - setContentWithTheme { - CustomListLocationsScreen( - state = - CustomListLocationsUiState.Content.Data( - newList = false, - locations = - listOf(RelayLocationListItem(selectedCountry, checked = true)), - ), - onRelaySelectionClick = mockedOnRelaySelectionClicked, - ) - } + initScreen( + state = + CustomListLocationsUiState.Content.Data( + newList = false, + locations = listOf(RelayLocationListItem(selectedCountry, checked = true)), + ), + onRelaySelectionClick = mockedOnRelaySelectionClicked, + ) // Act onNodeWithText(selectedCountry.name).performClick() @@ -126,16 +131,14 @@ class CustomListLocationsScreenTest { composeExtension.use { // Arrange val mockedSearchTermInput: (String) -> Unit = mockk(relaxed = true) - setContentWithTheme { - CustomListLocationsScreen( - state = - CustomListLocationsUiState.Content.Data( - newList = false, - locations = emptyList(), - ), - onSearchTermInput = mockedSearchTermInput, - ) - } + initScreen( + state = + CustomListLocationsUiState.Content.Data( + newList = false, + locations = emptyList(), + ), + onSearchTermInput = mockedSearchTermInput, + ) val mockSearchString = "SEARCH" // Act @@ -151,16 +154,14 @@ class CustomListLocationsScreenTest { // Arrange val mockedSearchTermInput: (String) -> Unit = mockk(relaxed = true) val mockSearchString = "SEARCH" - setContentWithTheme { - CustomListLocationsScreen( - state = - CustomListLocationsUiState.Content.Empty( - newList = false, - searchTerm = mockSearchString, - ), - onSearchTermInput = mockedSearchTermInput, - ) - } + initScreen( + state = + CustomListLocationsUiState.Content.Empty( + newList = false, + searchTerm = mockSearchString, + ), + onSearchTermInput = mockedSearchTermInput, + ) // Assert onNodeWithText(EMPTY_SEARCH.format(mockSearchString)).assertExists() @@ -171,15 +172,13 @@ class CustomListLocationsScreenTest { composeExtension.use { // Arrange val emptySearchString = "" - setContentWithTheme { - CustomListLocationsScreen( - state = - CustomListLocationsUiState.Content.Empty( - newList = false, - searchTerm = emptySearchString, - ) - ) - } + initScreen( + state = + CustomListLocationsUiState.Content.Empty( + newList = false, + searchTerm = emptySearchString, + ) + ) // Assert onNodeWithText(NO_LOCATIONS_FOUND_TEXT).assertExists() @@ -190,17 +189,15 @@ class CustomListLocationsScreenTest { composeExtension.use { // Arrange val mockOnSaveClick: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - CustomListLocationsScreen( - state = - CustomListLocationsUiState.Content.Data( - newList = false, - locations = emptyList(), - saveEnabled = true, - ), - onSaveClick = mockOnSaveClick, - ) - } + initScreen( + state = + CustomListLocationsUiState.Content.Data( + newList = false, + locations = emptyList(), + saveEnabled = true, + ), + onSaveClick = mockOnSaveClick, + ) // Act onNodeWithTag(SAVE_BUTTON_TEST_TAG).performClick() @@ -214,17 +211,15 @@ class CustomListLocationsScreenTest { composeExtension.use { // Arrange val mockOnSaveClick: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - CustomListLocationsScreen( - state = - CustomListLocationsUiState.Content.Data( - newList = false, - locations = emptyList(), - saveEnabled = false, - ), - onSaveClick = mockOnSaveClick, - ) - } + initScreen( + state = + CustomListLocationsUiState.Content.Data( + newList = false, + locations = emptyList(), + saveEnabled = false, + ), + onSaveClick = mockOnSaveClick, + ) // Act onNodeWithTag(SAVE_BUTTON_TEST_TAG).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreenTest.kt index 47e88bb52f54..f319ce92b3db 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreenTest.kt @@ -1,10 +1,10 @@ package net.mullvad.mullvadvpn.compose.screen -import androidx.compose.material3.SnackbarHostState import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -28,16 +28,28 @@ class CustomListsScreenTest { MockKAnnotations.init(this) } + private fun ComposeContext.initScreen( + state: CustomListsUiState = CustomListsUiState.Loading, + addCustomList: () -> Unit = {}, + openCustomList: (CustomList) -> Unit = {}, + onBackClick: () -> Unit = {}, + ) { + + setContentWithTheme { + CustomListsScreen( + state = state, + addCustomList = addCustomList, + openCustomList = openCustomList, + onBackClick = onBackClick, + ) + } + } + @Test fun givenLoadingStateShouldShowLoadingSpinner() = composeExtension.use { // Arrange - setContentWithTheme { - CustomListsScreen( - state = CustomListsUiState.Loading, - snackbarHostState = SnackbarHostState(), - ) - } + initScreen(state = CustomListsUiState.Loading) // Assert onNodeWithTag(CIRCULAR_PROGRESS_INDICATOR).assertExists() @@ -48,12 +60,7 @@ class CustomListsScreenTest { composeExtension.use { // Arrange val customLists = DUMMY_CUSTOM_LISTS - setContentWithTheme { - CustomListsScreen( - state = CustomListsUiState.Content(customLists = customLists), - snackbarHostState = SnackbarHostState(), - ) - } + initScreen(state = CustomListsUiState.Content(customLists = customLists)) // Assert onNodeWithText(customLists[0].name.value).assertExists() @@ -66,13 +73,10 @@ class CustomListsScreenTest { // Arrange val customLists = DUMMY_CUSTOM_LISTS val mockedAddCustomList: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - CustomListsScreen( - state = CustomListsUiState.Content(customLists = customLists), - snackbarHostState = SnackbarHostState(), - addCustomList = mockedAddCustomList, - ) - } + initScreen( + state = CustomListsUiState.Content(customLists = customLists), + addCustomList = mockedAddCustomList, + ) // Act onNodeWithTag(NEW_LIST_BUTTON_TEST_TAG).performClick() @@ -88,13 +92,10 @@ class CustomListsScreenTest { val customLists = DUMMY_CUSTOM_LISTS val clickedList = DUMMY_CUSTOM_LISTS[0] val mockedOpenCustomList: (CustomList) -> Unit = mockk(relaxed = true) - setContentWithTheme { - CustomListsScreen( - state = CustomListsUiState.Content(customLists = customLists), - snackbarHostState = SnackbarHostState(), - openCustomList = mockedOpenCustomList, - ) - } + initScreen( + state = CustomListsUiState.Content(customLists = customLists), + openCustomList = mockedOpenCustomList, + ) // Act onNodeWithText(clickedList.name.value).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt index cd1c03a59e1d..879d76ddd4cc 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt @@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.compose.screen import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -22,6 +23,20 @@ class DeviceRevokedScreenTest { MockKAnnotations.init(this) } + private fun ComposeContext.initScreen( + state: DeviceRevokedUiState, + onSettingsClicked: () -> Unit = {}, + onGoToLoginClicked: () -> Unit = {}, + ) { + setContentWithTheme { + DeviceRevokedScreen( + state = state, + onSettingsClicked = onSettingsClicked, + onGoToLoginClicked = onGoToLoginClicked, + ) + } + } + @Test fun testUnblockWarningShowingWhenSecured() = composeExtension.use { @@ -29,7 +44,7 @@ class DeviceRevokedScreenTest { val state = DeviceRevokedUiState.SECURED // Act - setContentWithTheme { DeviceRevokedScreen(state) } + initScreen(state) // Assert onNodeWithText(UNBLOCK_WARNING).assertExists() @@ -42,7 +57,7 @@ class DeviceRevokedScreenTest { val state = DeviceRevokedUiState.UNSECURED // Act - setContentWithTheme { DeviceRevokedScreen(state) } + initScreen(state) // Assert onNodeWithText(UNBLOCK_WARNING).assertDoesNotExist() @@ -54,9 +69,7 @@ class DeviceRevokedScreenTest { // Arrange val state = DeviceRevokedUiState.UNSECURED val mockOnGoToLoginClicked: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - DeviceRevokedScreen(state = state, onGoToLoginClicked = mockOnGoToLoginClicked) - } + initScreen(state = state, onGoToLoginClicked = mockOnGoToLoginClicked) // Act onNodeWithText(GO_TO_LOGIN_BUTTON_TEXT).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreenTest.kt index d737f5459f00..1c763219ea99 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreenTest.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.mockk import io.mockk.verify import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension @@ -13,6 +14,7 @@ import net.mullvad.mullvadvpn.compose.state.ApiAccessMethodTypes import net.mullvad.mullvadvpn.compose.state.EditApiAccessFormData import net.mullvad.mullvadvpn.compose.state.EditApiAccessMethodUiState import net.mullvad.mullvadvpn.compose.test.EDIT_API_ACCESS_NAME_INPUT +import net.mullvad.mullvadvpn.lib.model.Cipher import net.mullvad.mullvadvpn.lib.model.InvalidDataError import net.mullvad.mullvadvpn.lib.model.ParsePortError import org.junit.jupiter.api.Test @@ -22,21 +24,51 @@ import org.junit.jupiter.api.extension.RegisterExtension class EditApiAccessMethodScreenTest { @JvmField @RegisterExtension val composeExtension = createEdgeToEdgeComposeExtension() + private fun ComposeContext.initScreen( + state: EditApiAccessMethodUiState, + onNameChanged: (String) -> Unit = {}, + onTypeSelected: (ApiAccessMethodTypes) -> Unit = {}, + onIpChanged: (String) -> Unit = {}, + onPortChanged: (String) -> Unit = {}, + onPasswordChanged: (String) -> Unit = {}, + onCipherChange: (Cipher) -> Unit = {}, + onToggleAuthenticationEnabled: (Boolean) -> Unit = {}, + onUsernameChanged: (String) -> Unit = {}, + onTestMethod: () -> Unit = {}, + onAddMethod: () -> Unit = {}, + onNavigateBack: () -> Unit = {}, + ) { + setContentWithTheme { + EditApiAccessMethodScreen( + state = state, + onNameChanged = onNameChanged, + onTypeSelected = onTypeSelected, + onIpChanged = onIpChanged, + onPortChanged = onPortChanged, + onPasswordChanged = onPasswordChanged, + onCipherChange = onCipherChange, + onToggleAuthenticationEnabled = onToggleAuthenticationEnabled, + onUsernameChanged = onUsernameChanged, + onTestMethod = onTestMethod, + onAddMethod = onAddMethod, + onNavigateBack = onNavigateBack, + ) + } + } + @Test fun whenInEditModeAddButtonShouldSaySave() = composeExtension.use { // Arrange - setContentWithTheme { - EditApiAccessMethodScreen( - state = - EditApiAccessMethodUiState.Content( - editMode = true, - formData = EditApiAccessFormData.empty(), - hasChanges = false, - isTestingApiAccessMethod = false, - ) - ) - } + initScreen( + state = + EditApiAccessMethodUiState.Content( + editMode = true, + formData = EditApiAccessFormData.empty(), + hasChanges = false, + isTestingApiAccessMethod = false, + ) + ) // Assert onNodeWithText("Save").assertExists() @@ -46,17 +78,15 @@ class EditApiAccessMethodScreenTest { fun whenNotInEditModeAddButtonShouldSayAdd() = composeExtension.use { // Arrange - setContentWithTheme { - EditApiAccessMethodScreen( - state = - EditApiAccessMethodUiState.Content( - editMode = false, - formData = EditApiAccessFormData.empty(), - hasChanges = false, - isTestingApiAccessMethod = false, - ) - ) - } + initScreen( + state = + EditApiAccessMethodUiState.Content( + editMode = false, + formData = EditApiAccessFormData.empty(), + hasChanges = false, + isTestingApiAccessMethod = false, + ) + ) // Assert onNodeWithText("Add").assertExists() @@ -66,25 +96,23 @@ class EditApiAccessMethodScreenTest { fun whenNameInputHasErrorShouldShowError() = composeExtension.use { // Arrange - setContentWithTheme { - EditApiAccessMethodScreen( - state = - EditApiAccessMethodUiState.Content( - editMode = false, - formData = - EditApiAccessFormData( - name = "", - nameError = InvalidDataError.NameError.Required, - serverIp = "", - username = "", - password = "", - port = "", - ), - hasChanges = false, - isTestingApiAccessMethod = false, - ) - ) - } + initScreen( + state = + EditApiAccessMethodUiState.Content( + editMode = false, + formData = + EditApiAccessFormData( + name = "", + nameError = InvalidDataError.NameError.Required, + serverIp = "", + username = "", + password = "", + port = "", + ), + hasChanges = false, + isTestingApiAccessMethod = false, + ) + ) // Assert onNodeWithText("This field is required").assertExists() @@ -94,25 +122,23 @@ class EditApiAccessMethodScreenTest { fun whenServerInputIsNotIpAddressShouldShowError() = composeExtension.use { // Arrange - setContentWithTheme { - EditApiAccessMethodScreen( - state = - EditApiAccessMethodUiState.Content( - editMode = false, - formData = - EditApiAccessFormData( - name = "", - serverIp = "123", - serverIpError = InvalidDataError.ServerIpError.Invalid, - username = "", - password = "", - port = "", - ), - hasChanges = false, - isTestingApiAccessMethod = false, - ) - ) - } + initScreen( + state = + EditApiAccessMethodUiState.Content( + editMode = false, + formData = + EditApiAccessFormData( + name = "", + serverIp = "123", + serverIpError = InvalidDataError.ServerIpError.Invalid, + username = "", + password = "", + port = "", + ), + hasChanges = false, + isTestingApiAccessMethod = false, + ) + ) // Assert onNodeWithText("Please enter a valid IPv4 or IPv6 address").assertExists() @@ -122,28 +148,26 @@ class EditApiAccessMethodScreenTest { fun whenPortInputIsNotWithinRangeShouldShowError() = composeExtension.use { // Arrange - setContentWithTheme { - EditApiAccessMethodScreen( - state = - EditApiAccessMethodUiState.Content( - editMode = false, - formData = - EditApiAccessFormData( - name = "", - serverIp = "", - username = "", - password = "", - port = "1111111111", - portError = - InvalidDataError.PortError.Invalid( - ParsePortError.OutOfRange(1111111111) - ), - ), - hasChanges = false, - isTestingApiAccessMethod = false, - ) - ) - } + initScreen( + state = + EditApiAccessMethodUiState.Content( + editMode = false, + formData = + EditApiAccessFormData( + name = "", + serverIp = "", + username = "", + password = "", + port = "1111111111", + portError = + InvalidDataError.PortError.Invalid( + ParsePortError.OutOfRange(1111111111) + ), + ), + hasChanges = false, + isTestingApiAccessMethod = false, + ) + ) // Assert onNodeWithText("Please enter a valid remote server port").assertExists() @@ -155,18 +179,16 @@ class EditApiAccessMethodScreenTest { // Arrange val onNameChanged: (String) -> Unit = mockk(relaxed = true) val mockInput = "Name" - setContentWithTheme { - EditApiAccessMethodScreen( - state = - EditApiAccessMethodUiState.Content( - editMode = false, - formData = EditApiAccessFormData.empty(), - hasChanges = false, - isTestingApiAccessMethod = false, - ), - onNameChanged = onNameChanged, - ) - } + initScreen( + state = + EditApiAccessMethodUiState.Content( + editMode = false, + formData = EditApiAccessFormData.empty(), + hasChanges = false, + isTestingApiAccessMethod = false, + ), + onNameChanged = onNameChanged, + ) // Act onNodeWithTag(EDIT_API_ACCESS_NAME_INPUT).performTextInput(mockInput) @@ -179,26 +201,24 @@ class EditApiAccessMethodScreenTest { fun whenSocks5IsSelectedAndAuthenticationIsEnabledShouldShowUsernameAndPassword() = composeExtension.use { // Arrange - setContentWithTheme { - EditApiAccessMethodScreen( - state = - EditApiAccessMethodUiState.Content( - editMode = false, - formData = - EditApiAccessFormData( - name = "", - serverIp = "", - username = "", - password = "", - port = "", - enableAuthentication = true, - apiAccessMethodTypes = ApiAccessMethodTypes.SOCKS5_REMOTE, - ), - hasChanges = false, - isTestingApiAccessMethod = false, - ) - ) - } + initScreen( + state = + EditApiAccessMethodUiState.Content( + editMode = false, + formData = + EditApiAccessFormData( + name = "", + serverIp = "", + username = "", + password = "", + port = "", + enableAuthentication = true, + apiAccessMethodTypes = ApiAccessMethodTypes.SOCKS5_REMOTE, + ), + hasChanges = false, + isTestingApiAccessMethod = false, + ) + ) // Assert onNodeWithText("Username").assertExists() @@ -210,18 +230,16 @@ class EditApiAccessMethodScreenTest { composeExtension.use { // Arrange val onTestMethod: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - EditApiAccessMethodScreen( - state = - EditApiAccessMethodUiState.Content( - editMode = false, - formData = EditApiAccessFormData.empty(), - hasChanges = false, - isTestingApiAccessMethod = false, - ), - onTestMethod = onTestMethod, - ) - } + initScreen( + state = + EditApiAccessMethodUiState.Content( + editMode = false, + formData = EditApiAccessFormData.empty(), + hasChanges = false, + isTestingApiAccessMethod = false, + ), + onTestMethod = onTestMethod, + ) // Act onNodeWithText("Test method").performClick() @@ -235,18 +253,16 @@ class EditApiAccessMethodScreenTest { composeExtension.use { // Arrange val onAddMethod: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - EditApiAccessMethodScreen( - state = - EditApiAccessMethodUiState.Content( - editMode = false, - formData = EditApiAccessFormData.empty(), - hasChanges = false, - isTestingApiAccessMethod = false, - ), - onAddMethod = onAddMethod, - ) - } + initScreen( + state = + EditApiAccessMethodUiState.Content( + editMode = false, + formData = EditApiAccessFormData.empty(), + hasChanges = false, + isTestingApiAccessMethod = false, + ), + onAddMethod = onAddMethod, + ) // Act onNodeWithText("Add").performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreenTest.kt index 1207f2417bfd..f3a4e3b2af2a 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreenTest.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -29,11 +30,29 @@ class EditCustomListScreenTest { MockKAnnotations.init(this) } + private fun ComposeContext.initScreen( + state: EditCustomListUiState = EditCustomListUiState.Loading, + onDeleteList: (id: CustomListId, name: CustomListName) -> Unit = { _, _ -> }, + onNameClicked: (id: CustomListId, name: CustomListName) -> Unit = { _, _ -> }, + onLocationsClicked: (CustomListId) -> Unit = {}, + onBackClick: () -> Unit = {}, + ) { + setContentWithTheme { + EditCustomListScreen( + state = state, + onDeleteList = onDeleteList, + onNameClicked = onNameClicked, + onLocationsClicked = onLocationsClicked, + onBackClick = onBackClick, + ) + } + } + @Test fun givenLoadingStateShouldShowLoadingSpinner() = composeExtension.use { // Arrange - setContentWithTheme { EditCustomListScreen(state = EditCustomListUiState.Loading) } + initScreen() // Assert onNodeWithTag(CIRCULAR_PROGRESS_INDICATOR).assertExists() @@ -43,7 +62,7 @@ class EditCustomListScreenTest { fun givenNotFoundStateShouldShowNotFound() = composeExtension.use { // Arrange - setContentWithTheme { EditCustomListScreen(state = EditCustomListUiState.NotFound) } + initScreen(state = EditCustomListUiState.NotFound) // Assert onNodeWithText(NOT_FOUND_TEXT).assertExists() @@ -54,16 +73,15 @@ class EditCustomListScreenTest { composeExtension.use { // Arrange val customList = DUMMY_CUSTOM_LISTS[0] - setContentWithTheme { - EditCustomListScreen( - state = - EditCustomListUiState.Content( - id = customList.id, - name = customList.name, - locations = customList.locations, - ) - ) - } + + initScreen( + state = + EditCustomListUiState.Content( + id = customList.id, + name = customList.name, + locations = customList.locations, + ) + ) // Assert onNodeWithText(customList.name.value) @@ -74,16 +92,14 @@ class EditCustomListScreenTest { composeExtension.use { // Arrange val customList = DUMMY_CUSTOM_LISTS[0] - setContentWithTheme { - EditCustomListScreen( - state = - EditCustomListUiState.Content( - id = customList.id, - name = customList.name, - locations = customList.locations, - ) - ) - } + initScreen( + state = + EditCustomListUiState.Content( + id = customList.id, + name = customList.name, + locations = customList.locations, + ) + ) // Assert onNodeWithText(LOCATIONS_TEXT.format(customList.locations.size)) @@ -95,17 +111,15 @@ class EditCustomListScreenTest { // Arrange val mockedOnDelete: (CustomListId, CustomListName) -> Unit = mockk(relaxed = true) val customList = DUMMY_CUSTOM_LISTS[0] - setContentWithTheme { - EditCustomListScreen( - state = - EditCustomListUiState.Content( - id = customList.id, - name = customList.name, - locations = customList.locations, - ), - onDeleteList = mockedOnDelete, - ) - } + initScreen( + state = + EditCustomListUiState.Content( + id = customList.id, + name = customList.name, + locations = customList.locations, + ), + onDeleteList = mockedOnDelete, + ) // Act onNodeWithTag(TOP_BAR_DROPDOWN_BUTTON_TEST_TAG).performClick() @@ -121,17 +135,15 @@ class EditCustomListScreenTest { // Arrange val mockedOnNameClicked: (CustomListId, CustomListName) -> Unit = mockk(relaxed = true) val customList = DUMMY_CUSTOM_LISTS[0] - setContentWithTheme { - EditCustomListScreen( - state = - EditCustomListUiState.Content( - id = customList.id, - name = customList.name, - locations = customList.locations, - ), - onNameClicked = mockedOnNameClicked, - ) - } + initScreen( + state = + EditCustomListUiState.Content( + id = customList.id, + name = customList.name, + locations = customList.locations, + ), + onNameClicked = mockedOnNameClicked, + ) // Act onNodeWithText(customList.name.value).performClick() @@ -146,17 +158,15 @@ class EditCustomListScreenTest { // Arrange val mockedOnLocationsClicked: (CustomListId) -> Unit = mockk(relaxed = true) val customList = DUMMY_CUSTOM_LISTS[0] - setContentWithTheme { - EditCustomListScreen( - state = - EditCustomListUiState.Content( - id = customList.id, - name = customList.name, - locations = customList.locations, - ), - onLocationsClicked = mockedOnLocationsClicked, - ) - } + initScreen( + state = + EditCustomListUiState.Content( + id = customList.id, + name = customList.name, + locations = customList.locations, + ), + onLocationsClicked = mockedOnLocationsClicked, + ) // Act onNodeWithText(LOCATIONS_TEXT.format(customList.locations.size)).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreenTest.kt index 7ca3d6941e0d..2f16b27c237a 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreenTest.kt @@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.compose.screen import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -23,20 +24,37 @@ class FilterScreenTest { MockKAnnotations.init(this) } + private fun ComposeContext.initScreen( + state: RelayFilterUiState = RelayFilterUiState(), + onBackClick: () -> Unit = {}, + onApplyClick: () -> Unit = {}, + onSelectedOwnership: (ownership: Ownership?) -> Unit = {}, + onAllProviderCheckChange: (isChecked: Boolean) -> Unit = {}, + onSelectedProvider: (checked: Boolean, provider: Provider) -> Unit = { _, _ -> }, + ) { + setContentWithTheme { + FilterScreen( + state = state, + onBackClick = onBackClick, + onApplyClick = onApplyClick, + onSelectedOwnership = onSelectedOwnership, + onAllProviderCheckChange = onAllProviderCheckChange, + onSelectedProvider = onSelectedProvider, + ) + } + } + @Test fun testDefaultState() = composeExtension.use { - setContentWithTheme { - FilterScreen( - state = - RelayFilterUiState( - allProviders = DUMMY_RELAY_ALL_PROVIDERS, - selectedOwnership = null, - selectedProviders = DUMMY_SELECTED_PROVIDERS, - ), - onSelectedProvider = { _, _ -> }, - ) - } + initScreen( + state = + RelayFilterUiState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = null, + selectedProviders = DUMMY_SELECTED_PROVIDERS, + ) + ) onNodeWithText("Ownership").assertExists() onNodeWithText("Providers").assertExists() } @@ -44,17 +62,14 @@ class FilterScreenTest { @Test fun testIsAnyCellShowing() = composeExtension.use { - setContentWithTheme { - FilterScreen( - state = - RelayFilterUiState( - allProviders = DUMMY_RELAY_ALL_PROVIDERS, - selectedOwnership = null, - selectedProviders = DUMMY_SELECTED_PROVIDERS, - ), - onSelectedProvider = { _, _ -> }, - ) - } + initScreen( + state = + RelayFilterUiState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = null, + selectedProviders = DUMMY_SELECTED_PROVIDERS, + ) + ) onNodeWithText("Ownership").performClick() onNodeWithText("Any").assertExists() } @@ -62,17 +77,14 @@ class FilterScreenTest { @Test fun testIsMullvadCellShowing() = composeExtension.use { - setContentWithTheme { - FilterScreen( - state = - RelayFilterUiState( - allProviders = DUMMY_RELAY_ALL_PROVIDERS, - selectedOwnership = Ownership.MullvadOwned, - selectedProviders = DUMMY_SELECTED_PROVIDERS, - ), - onSelectedProvider = { _, _ -> }, - ) - } + initScreen( + state = + RelayFilterUiState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = Ownership.MullvadOwned, + selectedProviders = DUMMY_SELECTED_PROVIDERS, + ) + ) onNodeWithText("Ownership").performClick() onNodeWithText("Mullvad owned only").assertExists() } @@ -80,17 +92,14 @@ class FilterScreenTest { @Test fun testIsRentedCellShowing() = composeExtension.use { - setContentWithTheme { - FilterScreen( - state = - RelayFilterUiState( - allProviders = DUMMY_RELAY_ALL_PROVIDERS, - selectedOwnership = Ownership.Rented, - selectedProviders = DUMMY_SELECTED_PROVIDERS, - ), - onSelectedProvider = { _, _ -> }, - ) - } + initScreen( + state = + RelayFilterUiState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = Ownership.Rented, + selectedProviders = DUMMY_SELECTED_PROVIDERS, + ) + ) onNodeWithText("Ownership").performClick() onNodeWithText("Rented only").assertExists() } @@ -98,17 +107,14 @@ class FilterScreenTest { @Test fun testShowProviders() = composeExtension.use { - setContentWithTheme { - FilterScreen( - state = - RelayFilterUiState( - allProviders = DUMMY_RELAY_ALL_PROVIDERS, - selectedOwnership = null, - selectedProviders = DUMMY_SELECTED_PROVIDERS, - ), - onSelectedProvider = { _, _ -> }, - ) - } + initScreen( + state = + RelayFilterUiState( + allProviders = DUMMY_RELAY_ALL_PROVIDERS, + selectedOwnership = null, + selectedProviders = DUMMY_SELECTED_PROVIDERS, + ) + ) onNodeWithText("Providers").performClick() onNodeWithText("Creanova").assertExists() @@ -119,19 +125,16 @@ class FilterScreenTest { fun testApplyButtonClick() = composeExtension.use { val mockClickListener: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - FilterScreen( - state = - RelayFilterUiState( - allProviders = listOf(), - selectedOwnership = null, - selectedProviders = - listOf(Provider(ProviderId("31173"), Ownership.MullvadOwned)), - ), - onSelectedProvider = { _, _ -> }, - onApplyClick = mockClickListener, - ) - } + initScreen( + state = + RelayFilterUiState( + allProviders = listOf(), + selectedOwnership = null, + selectedProviders = + listOf(Provider(ProviderId("31173"), Ownership.MullvadOwned)), + ), + onApplyClick = mockClickListener, + ) onNodeWithText("Apply").performClick() verify { mockClickListener() } } diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt index 4294577836f8..ce683cc7fd21 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt @@ -1,9 +1,11 @@ package net.mullvad.mullvadvpn.compose.screen import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.mockk @@ -31,20 +33,36 @@ class OutOfTimeScreenTest { MockKAnnotations.init(this) } + private fun ComposeContext.initScreen( + state: OutOfTimeUiState = OutOfTimeUiState(), + onDisconnectClick: () -> Unit = {}, + onSitePaymentClick: () -> Unit = {}, + onRedeemVoucherClick: () -> Unit = {}, + onSettingsClick: () -> Unit = {}, + onAccountClick: () -> Unit = {}, + onPurchaseBillingProductClick: (ProductId) -> Unit = {}, + navigateToVerificationPendingDialog: () -> Unit = {}, + ) { + + setContentWithTheme { + OutOfTimeScreen( + state = state, + onDisconnectClick = onDisconnectClick, + onSitePaymentClick = onSitePaymentClick, + onRedeemVoucherClick = onRedeemVoucherClick, + onSettingsClick = onSettingsClick, + onAccountClick = onAccountClick, + onPurchaseBillingProductClick = onPurchaseBillingProductClick, + navigateToVerificationPendingDialog = navigateToVerificationPendingDialog, + ) + } + } + @Test fun testDisableSitePayment() = composeExtension.use { // Arrange - setContentWithTheme { - OutOfTimeScreen( - state = OutOfTimeUiState(deviceName = ""), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onDisconnectClick = {}, - ) - } + initScreen(state = OutOfTimeUiState(deviceName = "")) // Assert onNodeWithText( @@ -58,20 +76,18 @@ class OutOfTimeScreenTest { @Test fun testOpenAccountView() = composeExtension.use { + val mockClickListener: () -> Unit = mockk(relaxed = true) + // Arrange - setContentWithTheme { - OutOfTimeScreen( - state = OutOfTimeUiState(deviceName = "", showSitePayment = true), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onDisconnectClick = {}, - ) - } + initScreen( + state = OutOfTimeUiState(deviceName = "", showSitePayment = true), + onAccountClick = mockClickListener, + ) + + onNodeWithContentDescription(label = "Account").performClick() // Assert - onNodeWithText("Congrats!").assertDoesNotExist() + verify(exactly = 1) { mockClickListener.invoke() } } @Test @@ -79,16 +95,10 @@ class OutOfTimeScreenTest { composeExtension.use { // Arrange val mockClickListener: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - OutOfTimeScreen( - state = OutOfTimeUiState(deviceName = "", showSitePayment = true), - onSitePaymentClick = mockClickListener, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onDisconnectClick = {}, - ) - } + initScreen( + state = OutOfTimeUiState(deviceName = "", showSitePayment = true), + onSitePaymentClick = mockClickListener, + ) // Act onNodeWithText("Buy credit").performClick() @@ -102,16 +112,10 @@ class OutOfTimeScreenTest { composeExtension.use { // Arrange val mockClickListener: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - OutOfTimeScreen( - state = OutOfTimeUiState(deviceName = "", showSitePayment = true), - onSitePaymentClick = {}, - onRedeemVoucherClick = mockClickListener, - onSettingsClick = {}, - onAccountClick = {}, - onDisconnectClick = {}, - ) - } + initScreen( + state = OutOfTimeUiState(deviceName = "", showSitePayment = true), + onRedeemVoucherClick = mockClickListener, + ) // Act onNodeWithText("Redeem voucher").performClick() @@ -125,21 +129,15 @@ class OutOfTimeScreenTest { composeExtension.use { // Arrange val mockClickListener: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - OutOfTimeScreen( - state = - OutOfTimeUiState( - tunnelState = TunnelState.Connecting(null, null, emptyList()), - deviceName = "", - showSitePayment = true, - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onDisconnectClick = mockClickListener, - ) - } + initScreen( + state = + OutOfTimeUiState( + tunnelState = TunnelState.Connecting(null, null, emptyList()), + deviceName = "", + showSitePayment = true, + ), + onDisconnectClick = mockClickListener, + ) // Act onNodeWithText("Disconnect").performClick() @@ -152,20 +150,13 @@ class OutOfTimeScreenTest { fun testShowBillingErrorPaymentButton() = composeExtension.use { // Arrange - setContentWithTheme { - OutOfTimeScreen( - state = - OutOfTimeUiState( - showSitePayment = true, - billingPaymentState = PaymentState.Error.Billing, - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - ) - } + initScreen( + state = + OutOfTimeUiState( + showSitePayment = true, + billingPaymentState = PaymentState.Error.Billing, + ) + ) // Assert onNodeWithText("Add 30 days time").assertExists() @@ -178,21 +169,14 @@ class OutOfTimeScreenTest { val mockPaymentProduct: PaymentProduct = mockk() every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns null - setContentWithTheme { - OutOfTimeScreen( - state = - OutOfTimeUiState( - showSitePayment = true, - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - ) - } + initScreen( + state = + OutOfTimeUiState( + showSitePayment = true, + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), + ) + ) // Assert onNodeWithText("Add 30 days time ($10)").assertExists() @@ -205,16 +189,14 @@ class OutOfTimeScreenTest { val mockPaymentProduct: PaymentProduct = mockk() every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns PaymentStatus.PENDING - setContentWithTheme { - OutOfTimeScreen( - state = - OutOfTimeUiState( - showSitePayment = true, - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), - ) - ) - } + initScreen( + state = + OutOfTimeUiState( + showSitePayment = true, + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), + ) + ) // Assert onNodeWithText("Google Play payment pending").assertExists() @@ -228,17 +210,15 @@ class OutOfTimeScreenTest { every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns PaymentStatus.PENDING val mockNavigateToVerificationPending: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - OutOfTimeScreen( - state = - OutOfTimeUiState( - showSitePayment = true, - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), - ), - navigateToVerificationPendingDialog = mockNavigateToVerificationPending, - ) - } + initScreen( + state = + OutOfTimeUiState( + showSitePayment = true, + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), + ), + navigateToVerificationPendingDialog = mockNavigateToVerificationPending, + ) // Act onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick() @@ -255,16 +235,14 @@ class OutOfTimeScreenTest { val mockPaymentProduct: PaymentProduct = mockk() every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns PaymentStatus.VERIFICATION_IN_PROGRESS - setContentWithTheme { - OutOfTimeScreen( - state = - OutOfTimeUiState( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), - showSitePayment = true, - ) - ) - } + initScreen( + state = + OutOfTimeUiState( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), + showSitePayment = true, + ) + ) // Assert onNodeWithText("Verifying purchase").assertExists() @@ -279,21 +257,15 @@ class OutOfTimeScreenTest { every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.productId } returns ProductId("PRODUCT_ID") every { mockPaymentProduct.status } returns null - setContentWithTheme { - OutOfTimeScreen( - state = - OutOfTimeUiState( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), - showSitePayment = true, - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = clickHandler, - ) - } + initScreen( + state = + OutOfTimeUiState( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)), + showSitePayment = true, + ), + onPurchaseBillingProductClick = clickHandler, + ) // Act onNodeWithText("Add 30 days time ($10)").performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ServerIpOverridesScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ServerIpOverridesScreenTest.kt index 8d33d848f151..0b116161846b 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ServerIpOverridesScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ServerIpOverridesScreenTest.kt @@ -1,10 +1,10 @@ package net.mullvad.mullvadvpn.compose.screen -import androidx.compose.runtime.Composable import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -30,9 +30,7 @@ class ServerIpOverridesScreenTest { MockKAnnotations.init(this) } - @Suppress("TestFunctionName") - @Composable - private fun ScreenWithDefault( + private fun ComposeContext.initScreen( state: ServerIpOverridesUiState, onBackClick: () -> Unit = {}, onInfoClick: () -> Unit = {}, @@ -40,43 +38,43 @@ class ServerIpOverridesScreenTest { onImportByFile: () -> Unit = {}, onImportByText: () -> Unit = {}, ) { - ServerIpOverridesScreen( - state = state, - onBackClick = onBackClick, - onInfoClick = onInfoClick, - onResetOverridesClick = onResetOverridesClick, - onImportByFile = onImportByFile, - onImportByText = onImportByText, - ) + setContentWithTheme { + ServerIpOverridesScreen( + state = state, + onBackClick = onBackClick, + onInfoClick = onInfoClick, + onResetOverridesClick = onResetOverridesClick, + onImportByFile = onImportByFile, + onImportByText = onImportByText, + ) + } } @Test - fun ensure_overrides_inactive_is_displayed() = + fun ensureOverridesInactiveIsDisplayed() = composeExtension.use { // Arrange - setContentWithTheme { - ScreenWithDefault(state = ServerIpOverridesUiState.Loaded(false)) - } + initScreen(state = ServerIpOverridesUiState.Loaded(false)) // Assert onNodeWithText("Overrides inactive").assertExists() } @Test - fun ensure_overrides_active_is_displayed() = + fun ensureOverridesActiveIsDisplayed() = composeExtension.use { // Arrange - setContentWithTheme { ScreenWithDefault(state = ServerIpOverridesUiState.Loaded(true)) } + initScreen(state = ServerIpOverridesUiState.Loaded(true)) // Assert onNodeWithText("Overrides active").assertExists() } @Test - fun ensure_overrides_active_shows_warning_on_import() = + fun ensureOverridesActiveShowsWarningOnImport() = composeExtension.use { // Arrange - setContentWithTheme { ScreenWithDefault(state = ServerIpOverridesUiState.Loaded(true)) } + initScreen(state = ServerIpOverridesUiState.Loaded(true)) // Act onNodeWithTag(testTag = SERVER_IP_OVERRIDE_IMPORT_TEST_TAG).performClick() @@ -89,16 +87,11 @@ class ServerIpOverridesScreenTest { } @Test - fun ensure_info_click_works() = + fun ensureInfoClickWorks() = composeExtension.use { // Arrange val clickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ScreenWithDefault( - state = ServerIpOverridesUiState.Loaded(false), - onInfoClick = clickHandler, - ) - } + initScreen(state = ServerIpOverridesUiState.Loaded(false), onInfoClick = clickHandler) // Act onNodeWithTag(SERVER_IP_OVERRIDE_INFO_TEST_TAG).performClick() @@ -108,16 +101,14 @@ class ServerIpOverridesScreenTest { } @Test - fun ensure_reset_click_works() = + fun ensureResetClickWorks() = composeExtension.use { // Arrange val clickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ScreenWithDefault( - state = ServerIpOverridesUiState.Loaded(true), - onResetOverridesClick = clickHandler, - ) - } + initScreen( + state = ServerIpOverridesUiState.Loaded(true), + onResetOverridesClick = clickHandler, + ) // Act onNodeWithTag(SERVER_IP_OVERRIDE_MORE_VERT_TEST_TAG).performClick() @@ -128,16 +119,14 @@ class ServerIpOverridesScreenTest { } @Test - fun ensure_import_by_file_works() = + fun ensureImportByFileWorks() = composeExtension.use { // Arrange val clickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ScreenWithDefault( - state = ServerIpOverridesUiState.Loaded(false), - onImportByFile = clickHandler, - ) - } + initScreen( + state = ServerIpOverridesUiState.Loaded(false), + onImportByFile = clickHandler, + ) // Act onNodeWithTag(SERVER_IP_OVERRIDE_IMPORT_TEST_TAG).performClick() @@ -148,16 +137,14 @@ class ServerIpOverridesScreenTest { } @Test - fun ensure_import_by_text() = + fun ensureImportByText() = composeExtension.use { // Arrange val clickHandler: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - ScreenWithDefault( - state = ServerIpOverridesUiState.Loaded(false), - onImportByText = clickHandler, - ) - } + initScreen( + state = ServerIpOverridesUiState.Loaded(false), + onImportByText = clickHandler, + ) // Act onNodeWithTag(SERVER_IP_OVERRIDE_IMPORT_TEST_TAG).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreenTest.kt index 464fa1181a26..b343c44c9558 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreenTest.kt @@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.compose.screen import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension import net.mullvad.mullvadvpn.compose.setContentWithTheme @@ -20,24 +21,48 @@ class SettingsScreenTest { MockKAnnotations.init(this) } + private fun ComposeContext.initScreen( + state: SettingsUiState, + onVpnSettingCellClick: () -> Unit = {}, + onSplitTunnelingCellClick: () -> Unit = {}, + onAppInfoClick: () -> Unit = {}, + onReportProblemCellClick: () -> Unit = {}, + onApiAccessClick: () -> Unit = {}, + onMultihopClick: () -> Unit = {}, + onDaitaClick: () -> Unit = {}, + onBackClick: () -> Unit = {}, + ) { + setContentWithTheme { + SettingsScreen( + state = state, + onVpnSettingCellClick = onVpnSettingCellClick, + onSplitTunnelingCellClick = onSplitTunnelingCellClick, + onAppInfoClick = onAppInfoClick, + onReportProblemCellClick = onReportProblemCellClick, + onApiAccessClick = onApiAccessClick, + onMultihopClick = onMultihopClick, + onDaitaClick = onDaitaClick, + onBackClick = onBackClick, + ) + } + } + @Test @OptIn(ExperimentalMaterial3Api::class) fun testLoggedInState() = composeExtension.use { // Arrange - setContentWithTheme { - SettingsScreen( - state = - SettingsUiState( - appVersion = "", - isLoggedIn = true, - isSupportedVersion = true, - isPlayBuild = false, - multihopEnabled = false, - isDaitaEnabled = false, - ) - ) - } + initScreen( + state = + SettingsUiState( + appVersion = "", + isLoggedIn = true, + isSupportedVersion = true, + isPlayBuild = false, + multihopEnabled = false, + isDaitaEnabled = false, + ) + ) // Assert onNodeWithText("VPN settings").assertExists() onNodeWithText("Split tunneling").assertExists() @@ -50,19 +75,17 @@ class SettingsScreenTest { fun testLoggedOutState() = composeExtension.use { // Arrange - setContentWithTheme { - SettingsScreen( - state = - SettingsUiState( - appVersion = "", - isLoggedIn = false, - isSupportedVersion = true, - isPlayBuild = false, - multihopEnabled = false, - isDaitaEnabled = false, - ) - ) - } + initScreen( + state = + SettingsUiState( + appVersion = "", + isLoggedIn = false, + isSupportedVersion = true, + isPlayBuild = false, + multihopEnabled = false, + isDaitaEnabled = false, + ) + ) // Assert onNodeWithText("VPN settings").assertDoesNotExist() onNodeWithText("Split tunneling").assertDoesNotExist() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ShadowsocksSettingsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ShadowsocksSettingsScreenTest.kt index 23d94f58c6a3..1340e96c5880 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ShadowsocksSettingsScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ShadowsocksSettingsScreenTest.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.mockk import io.mockk.verify import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension @@ -19,13 +20,27 @@ import org.junit.jupiter.api.extension.RegisterExtension class ShadowsocksSettingsScreenTest { @JvmField @RegisterExtension val composeExtension = createEdgeToEdgeComposeExtension() + private fun ComposeContext.initScreen( + state: ShadowsocksSettingsState = ShadowsocksSettingsState(), + navigateToCustomPortDialog: () -> Unit = {}, + onObfuscationPortSelected: (Constraint) -> Unit = {}, + onBackClick: () -> Unit = {}, + ) { + setContentWithTheme { + ShadowsocksSettingsScreen( + state = state, + navigateToCustomPortDialog = navigateToCustomPortDialog, + onObfuscationPortSelected = onObfuscationPortSelected, + onBackClick = onBackClick, + ) + } + } + @Test fun testShowShadowsocksCustomPort() = composeExtension.use { // Arrange - setContentWithTheme { - ShadowsocksSettingsScreen(state = ShadowsocksSettingsState(customPort = Port(4000))) - } + initScreen(state = ShadowsocksSettingsState(customPort = Port(4000))) // Assert onNodeWithText("4000").assertExists() @@ -36,16 +51,14 @@ class ShadowsocksSettingsScreenTest { composeExtension.use { // Arrange val onObfuscationPortSelected: (Constraint) -> Unit = mockk(relaxed = true) - setContentWithTheme { - ShadowsocksSettingsScreen( - state = - ShadowsocksSettingsState( - port = Constraint.Only(Port(4000)), - customPort = Port(4000), - ), - onObfuscationPortSelected = onObfuscationPortSelected, - ) - } + initScreen( + state = + ShadowsocksSettingsState( + port = Constraint.Only(Port(4000)), + customPort = Port(4000), + ), + onObfuscationPortSelected = onObfuscationPortSelected, + ) // Act onNodeWithTag(testTag = SHADOWSOCKS_CUSTOM_PORT_TEXT_TEST_TAG).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreenTest.kt index 500349c861d8..8215ccde6950 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreenTest.kt @@ -1,8 +1,10 @@ package net.mullvad.mullvadvpn.compose.screen +import android.graphics.Bitmap import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.unmockkAll @@ -30,13 +32,33 @@ class SplitTunnelingScreenTest { unmockkAll() } + private fun ComposeContext.initScreen( + state: SplitTunnelingUiState, + onEnableSplitTunneling: (Boolean) -> Unit = {}, + onShowSystemAppsClick: (show: Boolean) -> Unit = {}, + onExcludeAppClick: (packageName: String) -> Unit = {}, + onIncludeAppClick: (packageName: String) -> Unit = {}, + onBackClick: () -> Unit = {}, + onResolveIcon: (String) -> Bitmap? = { null }, + ) { + setContentWithTheme { + SplitTunnelingScreen( + state = state, + onEnableSplitTunneling = onEnableSplitTunneling, + onShowSystemAppsClick = onShowSystemAppsClick, + onExcludeAppClick = onExcludeAppClick, + onIncludeAppClick = onIncludeAppClick, + onBackClick = onBackClick, + onResolveIcon = onResolveIcon, + ) + } + } + @Test fun testLoadingState() = composeExtension.use { // Arrange - setContentWithTheme { - SplitTunnelingScreen(state = SplitTunnelingUiState.Loading(enabled = true)) - } + initScreen(state = SplitTunnelingUiState.Loading(enabled = true)) // Assert onNodeWithText(TITLE).assertExists() @@ -62,17 +84,15 @@ class SplitTunnelingScreenTest { iconRes = 0, name = INCLUDED_APP_NAME, ) - setContentWithTheme { - SplitTunnelingScreen( - state = - SplitTunnelingUiState.ShowAppList( - enabled = true, - excludedApps = listOf(excludedApp), - includedApps = listOf(includedApp), - showSystemApps = false, - ) - ) - } + initScreen( + state = + SplitTunnelingUiState.ShowAppList( + enabled = true, + excludedApps = listOf(excludedApp), + includedApps = listOf(includedApp), + showSystemApps = false, + ) + ) // Assert onNodeWithText(TITLE).assertExists() @@ -94,17 +114,15 @@ class SplitTunnelingScreenTest { iconRes = 0, name = INCLUDED_APP_NAME, ) - setContentWithTheme { - SplitTunnelingScreen( - state = - SplitTunnelingUiState.ShowAppList( - enabled = true, - excludedApps = emptyList(), - includedApps = listOf(includedApp), - showSystemApps = false, - ) - ) - } + initScreen( + state = + SplitTunnelingUiState.ShowAppList( + enabled = true, + excludedApps = emptyList(), + includedApps = listOf(includedApp), + showSystemApps = false, + ) + ) // Assert onNodeWithText(TITLE).assertExists() @@ -133,18 +151,16 @@ class SplitTunnelingScreenTest { name = INCLUDED_APP_NAME, ) val mockedClickHandler: (String) -> Unit = mockk(relaxed = true) - setContentWithTheme { - SplitTunnelingScreen( - state = - SplitTunnelingUiState.ShowAppList( - enabled = true, - excludedApps = listOf(excludedApp), - includedApps = listOf(includedApp), - showSystemApps = false, - ), - onExcludeAppClick = mockedClickHandler, - ) - } + initScreen( + state = + SplitTunnelingUiState.ShowAppList( + enabled = true, + excludedApps = listOf(excludedApp), + includedApps = listOf(includedApp), + showSystemApps = false, + ), + onExcludeAppClick = mockedClickHandler, + ) // Act onNodeWithText(INCLUDED_APP_NAME).performClick() @@ -170,18 +186,16 @@ class SplitTunnelingScreenTest { name = INCLUDED_APP_NAME, ) val mockedClickHandler: (String) -> Unit = mockk(relaxed = true) - setContentWithTheme { - SplitTunnelingScreen( - state = - SplitTunnelingUiState.ShowAppList( - enabled = true, - excludedApps = listOf(excludedApp), - includedApps = listOf(includedApp), - showSystemApps = false, - ), - onIncludeAppClick = mockedClickHandler, - ) - } + initScreen( + state = + SplitTunnelingUiState.ShowAppList( + enabled = true, + excludedApps = listOf(excludedApp), + includedApps = listOf(includedApp), + showSystemApps = false, + ), + onIncludeAppClick = mockedClickHandler, + ) // Act onNodeWithText(EXCLUDED_APP_NAME).performClick() @@ -207,18 +221,16 @@ class SplitTunnelingScreenTest { name = INCLUDED_APP_NAME, ) val mockedClickHandler: (Boolean) -> Unit = mockk(relaxed = true) - setContentWithTheme { - SplitTunnelingScreen( - state = - SplitTunnelingUiState.ShowAppList( - enabled = true, - excludedApps = listOf(excludedApp), - includedApps = listOf(includedApp), - showSystemApps = false, - ), - onShowSystemAppsClick = mockedClickHandler, - ) - } + initScreen( + state = + SplitTunnelingUiState.ShowAppList( + enabled = true, + excludedApps = listOf(excludedApp), + includedApps = listOf(includedApp), + showSystemApps = false, + ), + onShowSystemAppsClick = mockedClickHandler, + ) // Act onNodeWithText(SHOW_SYSTEM_APPS).performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/Udp2TcpSettingsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/Udp2TcpSettingsScreenTest.kt index 77b9965b26b0..1c16ff0bd170 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/Udp2TcpSettingsScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/Udp2TcpSettingsScreenTest.kt @@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.compose.screen import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.coVerify import io.mockk.mockk import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension @@ -18,17 +19,31 @@ import org.junit.jupiter.api.extension.RegisterExtension class Udp2TcpSettingsScreenTest { @JvmField @RegisterExtension val composeExtension = createEdgeToEdgeComposeExtension() + private fun ComposeContext.initScreen( + state: Udp2TcpSettingsState = Udp2TcpSettingsState(), + onObfuscationPortSelected: (Constraint) -> Unit = {}, + navigateUdp2TcpInfo: () -> Unit = {}, + onBackClick: () -> Unit = {}, + ) { + setContentWithTheme { + Udp2TcpSettingsScreen( + state = state, + onObfuscationPortSelected = onObfuscationPortSelected, + navigateUdp2TcpInfo = navigateUdp2TcpInfo, + onBackClick = onBackClick, + ) + } + } + @Test fun testSelectTcpOverUdpPortOption() = composeExtension.use { // Arrange val onObfuscationPortSelected: (Constraint) -> Unit = mockk(relaxed = true) - setContentWithTheme { - Udp2TcpSettingsScreen( - state = Udp2TcpSettingsState(port = Constraint.Any), - onObfuscationPortSelected = onObfuscationPortSelected, - ) - } + initScreen( + state = Udp2TcpSettingsState(port = Constraint.Any), + onObfuscationPortSelected = onObfuscationPortSelected, + ) // Act onNodeWithTagAndText( diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt index 19802571f634..43c5559a3852 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt @@ -7,6 +7,7 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollToNode +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.verify @@ -23,6 +24,7 @@ import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_WIREGUARD_OBFUSCATION_TITLE import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG import net.mullvad.mullvadvpn.lib.model.Constraint import net.mullvad.mullvadvpn.lib.model.Mtu +import net.mullvad.mullvadvpn.lib.model.ObfuscationMode import net.mullvad.mullvadvpn.lib.model.Port import net.mullvad.mullvadvpn.lib.model.PortRange import net.mullvad.mullvadvpn.lib.model.QuantumResistantState @@ -41,11 +43,75 @@ class VpnSettingsScreenTest { MockKAnnotations.init(this) } + private fun ComposeContext.initScreen( + state: VpnSettingsUiState = VpnSettingsUiState.createDefault(), + navigateToContentBlockersInfo: () -> Unit = {}, + navigateToAutoConnectScreen: () -> Unit = {}, + navigateToCustomDnsInfo: () -> Unit = {}, + navigateToMalwareInfo: () -> Unit = {}, + navigateToObfuscationInfo: () -> Unit = {}, + navigateToQuantumResistanceInfo: () -> Unit = {}, + navigateToWireguardPortInfo: (availablePortRanges: List) -> Unit = {}, + navigateToLocalNetworkSharingInfo: () -> Unit = {}, + navigateToWireguardPortDialog: () -> Unit = {}, + navigateToServerIpOverrides: () -> Unit = {}, + onToggleBlockTrackers: (Boolean) -> Unit = {}, + onToggleBlockAds: (Boolean) -> Unit = {}, + onToggleBlockMalware: (Boolean) -> Unit = {}, + onToggleLocalNetworkSharing: (Boolean) -> Unit = {}, + onToggleBlockAdultContent: (Boolean) -> Unit = {}, + onToggleBlockGambling: (Boolean) -> Unit = {}, + onToggleBlockSocialMedia: (Boolean) -> Unit = {}, + navigateToMtuDialog: (mtu: Mtu?) -> Unit = {}, + navigateToDns: (index: Int?, address: String?) -> Unit = { _, _ -> }, + onToggleDnsClick: (Boolean) -> Unit = {}, + onBackClick: () -> Unit = {}, + onSelectObfuscationMode: (obfuscationMode: ObfuscationMode) -> Unit = {}, + onSelectQuantumResistanceSetting: (quantumResistant: QuantumResistantState) -> Unit = {}, + onWireguardPortSelected: (port: Constraint) -> Unit = {}, + navigateToShadowSocksSettings: () -> Unit = {}, + navigateToUdp2TcpSettings: () -> Unit = {}, + onToggleAutoStartAndConnectOnBoot: (Boolean) -> Unit = {}, + ) { + setContentWithTheme { + VpnSettingsScreen( + state = state, + navigateToContentBlockersInfo = navigateToContentBlockersInfo, + navigateToAutoConnectScreen = navigateToAutoConnectScreen, + navigateToCustomDnsInfo = navigateToCustomDnsInfo, + navigateToMalwareInfo = navigateToMalwareInfo, + navigateToObfuscationInfo = navigateToObfuscationInfo, + navigateToQuantumResistanceInfo = navigateToQuantumResistanceInfo, + navigateToWireguardPortInfo = navigateToWireguardPortInfo, + navigateToLocalNetworkSharingInfo = navigateToLocalNetworkSharingInfo, + navigateToWireguardPortDialog = navigateToWireguardPortDialog, + navigateToServerIpOverrides = navigateToServerIpOverrides, + onToggleBlockTrackers = onToggleBlockTrackers, + onToggleBlockAds = onToggleBlockAds, + onToggleBlockMalware = onToggleBlockMalware, + onToggleLocalNetworkSharing = onToggleLocalNetworkSharing, + onToggleBlockAdultContent = onToggleBlockAdultContent, + onToggleBlockGambling = onToggleBlockGambling, + onToggleBlockSocialMedia = onToggleBlockSocialMedia, + navigateToMtuDialog = navigateToMtuDialog, + navigateToDns = navigateToDns, + onToggleDnsClick = onToggleDnsClick, + onBackClick = onBackClick, + onSelectObfuscationMode = onSelectObfuscationMode, + onSelectQuantumResistanceSetting = onSelectQuantumResistanceSetting, + onWireguardPortSelected = onWireguardPortSelected, + navigateToShadowSocksSettings = navigateToShadowSocksSettings, + navigateToUdp2TcpSettings = navigateToUdp2TcpSettings, + onToggleAutoStartAndConnectOnBoot = onToggleAutoStartAndConnectOnBoot, + ) + } + } + @Test fun testDefaultState() = composeExtension.use { // Arrange - setContentWithTheme { VpnSettingsScreen(state = VpnSettingsUiState.createDefault()) } + initScreen(state = VpnSettingsUiState.createDefault()) onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) @@ -61,14 +127,12 @@ class VpnSettingsScreenTest { fun testMtuCustomValue() = composeExtension.use { // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - mtu = Mtu.fromString(VALID_DUMMY_MTU_VALUE).getOrNull()!! - ) - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + mtu = Mtu.fromString(VALID_DUMMY_MTU_VALUE).getOrNull()!! + ) + ) onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) @@ -81,20 +145,18 @@ class VpnSettingsScreenTest { fun testCustomDnsAddressesAndAddButtonVisibleWhenCustomDnsEnabled() = composeExtension.use { // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = true, - customDnsItems = - listOf( - CustomDnsItem(address = DUMMY_DNS_ADDRESS, false), - CustomDnsItem(address = DUMMY_DNS_ADDRESS_2, false), - CustomDnsItem(address = DUMMY_DNS_ADDRESS_3, false), - ), - ) - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = true, + customDnsItems = + listOf( + CustomDnsItem(address = DUMMY_DNS_ADDRESS, false), + CustomDnsItem(address = DUMMY_DNS_ADDRESS_2, false), + CustomDnsItem(address = DUMMY_DNS_ADDRESS_3, false), + ), + ) + ) // Assert onNodeWithText(DUMMY_DNS_ADDRESS).assertExists() @@ -107,16 +169,13 @@ class VpnSettingsScreenTest { fun testCustomDnsAddressesAndAddButtonNotVisibleWhenCustomDnsDisabled() = composeExtension.use { // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = false, - customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, false)), - ) - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = false, + customDnsItems = listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, false)), + ) + ) onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) // Assert @@ -128,17 +187,15 @@ class VpnSettingsScreenTest { fun testLanWarningNotShownWhenLanTrafficEnabledAndLocalAddressIsUsed() = composeExtension.use { // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = true, - isLocalNetworkSharingEnabled = true, - customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)), - ) - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = true, + isLocalNetworkSharingEnabled = true, + customDnsItems = + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)), + ) + ) // Assert onNodeWithContentDescription(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() @@ -148,16 +205,14 @@ class VpnSettingsScreenTest { fun testLanWarningNotShowedWhenLanTrafficDisabledAndLocalAddressIsNotUsed() = composeExtension.use { // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = true, - customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)), - ) - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = true, + customDnsItems = + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)), + ) + ) // Assert onNodeWithContentDescription(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() @@ -167,16 +222,14 @@ class VpnSettingsScreenTest { fun testLanWarningNotShowedWhenLanTrafficEnabledAndLocalAddressIsNotUsed() = composeExtension.use { // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = true, - customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)), - ) - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = true, + customDnsItems = + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)), + ) + ) // Assert onNodeWithContentDescription(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist() @@ -186,16 +239,14 @@ class VpnSettingsScreenTest { fun testLanWarningShowedWhenAllowLanEnabledAndLocalDnsAddressIsUsed() = composeExtension.use { // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - isCustomDnsEnabled = true, - customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)), - ) - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + isCustomDnsEnabled = true, + customDnsItems = + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)), + ) + ) // Assert onNodeWithContentDescription(LOCAL_DNS_SERVER_WARNING).assertExists() @@ -205,14 +256,10 @@ class VpnSettingsScreenTest { fun testShowSelectedTunnelQuantumOption() = composeExtension.use { // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - quantumResistant = QuantumResistantState.On - ) - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault(quantumResistant = QuantumResistantState.On) + ) onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) .performScrollToNode(hasTestTag(LAZY_LIST_QUANTUM_ITEM_OFF_TEST_TAG)) @@ -227,15 +274,11 @@ class VpnSettingsScreenTest { // Arrange val mockSelectQuantumResistantSettingListener: (QuantumResistantState) -> Unit = mockk(relaxed = true) - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - quantumResistant = QuantumResistantState.Auto - ), - onSelectQuantumResistanceSetting = mockSelectQuantumResistantSettingListener, - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault(quantumResistant = QuantumResistantState.Auto), + onSelectQuantumResistanceSetting = mockSelectQuantumResistantSettingListener, + ) onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) .performScrollToNode(hasTestTag(LAZY_LIST_QUANTUM_ITEM_OFF_TEST_TAG)) @@ -251,14 +294,12 @@ class VpnSettingsScreenTest { fun testShowWireguardPortOptions() = composeExtension.use { // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - selectedWireguardPort = Constraint.Only(Port(53)) - ) - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + selectedWireguardPort = Constraint.Only(Port(53)) + ) + ) // Act onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) @@ -280,15 +321,13 @@ class VpnSettingsScreenTest { // Arrange val mockSelectWireguardPortSelectionListener: (Constraint) -> Unit = mockk(relaxed = true) - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - selectedWireguardPort = Constraint.Only(Port(53)) - ), - onWireguardPortSelected = mockSelectWireguardPortSelectionListener, - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + selectedWireguardPort = Constraint.Only(Port(53)) + ), + onWireguardPortSelected = mockSelectWireguardPortSelectionListener, + ) // Act onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) @@ -311,11 +350,7 @@ class VpnSettingsScreenTest { fun testShowWireguardCustomPort() = composeExtension.use { // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = VpnSettingsUiState.createDefault(customWireguardPort = Port(4000)) - ) - } + initScreen(state = VpnSettingsUiState.createDefault(customWireguardPort = Port(4000))) // Act onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) @@ -330,16 +365,14 @@ class VpnSettingsScreenTest { composeExtension.use { // Arrange val onWireguardPortSelected: (Constraint) -> Unit = mockk(relaxed = true) - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - selectedWireguardPort = Constraint.Only(Port(4000)), - customWireguardPort = Port(4000), - ), - onWireguardPortSelected = onWireguardPortSelected, - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + selectedWireguardPort = Constraint.Only(Port(4000)), + customWireguardPort = Port(4000), + ), + onWireguardPortSelected = onWireguardPortSelected, + ) // Act onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) @@ -357,12 +390,10 @@ class VpnSettingsScreenTest { composeExtension.use { // Arrange val mockedClickHandler: (Mtu?) -> Unit = mockk(relaxed = true) - setContentWithTheme { - VpnSettingsScreen( - state = VpnSettingsUiState.createDefault(), - navigateToMtuDialog = mockedClickHandler, - ) - } + initScreen( + state = VpnSettingsUiState.createDefault(), + navigateToMtuDialog = mockedClickHandler, + ) onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) @@ -379,12 +410,10 @@ class VpnSettingsScreenTest { composeExtension.use { // Arrange val mockedClickHandler: (Int?, String?) -> Unit = mockk(relaxed = true) - setContentWithTheme { - VpnSettingsScreen( - state = VpnSettingsUiState.createDefault(isCustomDnsEnabled = true), - navigateToDns = mockedClickHandler, - ) - } + initScreen( + state = VpnSettingsUiState.createDefault(isCustomDnsEnabled = true), + navigateToDns = mockedClickHandler, + ) // Act onNodeWithText("Add a server").performClick() @@ -399,12 +428,10 @@ class VpnSettingsScreenTest { val mockedNavigateToObfuscationInfo: () -> Unit = mockk(relaxed = true) // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = VpnSettingsUiState.createDefault(), - navigateToObfuscationInfo = mockedNavigateToObfuscationInfo, - ) - } + initScreen( + state = VpnSettingsUiState.createDefault(), + navigateToObfuscationInfo = mockedNavigateToObfuscationInfo, + ) // Act onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) @@ -421,15 +448,12 @@ class VpnSettingsScreenTest { val mockedShowTunnelQuantumInfoClick: () -> Unit = mockk(relaxed = true) // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = VpnSettingsUiState.createDefault(), - navigateToQuantumResistanceInfo = mockedShowTunnelQuantumInfoClick, - ) - } + initScreen( + state = VpnSettingsUiState.createDefault(), + navigateToQuantumResistanceInfo = mockedShowTunnelQuantumInfoClick, + ) // Act - onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) .performScrollToNode(hasTestTag(LAZY_LIST_QUANTUM_ITEM_ON_TEST_TAG)) onNodeWithText("Quantum-resistant tunnel").performClick() @@ -444,12 +468,10 @@ class VpnSettingsScreenTest { val mockedClickHandler: (List) -> Unit = mockk(relaxed = true) // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = VpnSettingsUiState.createDefault(), - navigateToWireguardPortInfo = mockedClickHandler, - ) - } + initScreen( + state = VpnSettingsUiState.createDefault(), + navigateToWireguardPortInfo = mockedClickHandler, + ) onNodeWithText("WireGuard port").performClick() @@ -462,12 +484,10 @@ class VpnSettingsScreenTest { val mockedClickHandler: () -> Unit = mockk(relaxed = true) // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = VpnSettingsUiState.createDefault(), - navigateToWireguardPortDialog = mockedClickHandler, - ) - } + initScreen( + state = VpnSettingsUiState.createDefault(), + navigateToWireguardPortDialog = mockedClickHandler, + ) onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_NUMBER_TEST_TAG)) @@ -482,12 +502,10 @@ class VpnSettingsScreenTest { composeExtension.use { // Arrange val mockOnShowCustomPortDialog: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - VpnSettingsScreen( - state = VpnSettingsUiState.createDefault(), - navigateToWireguardPortDialog = mockOnShowCustomPortDialog, - ) - } + initScreen( + state = VpnSettingsUiState.createDefault(), + navigateToWireguardPortDialog = mockOnShowCustomPortDialog, + ) // Act onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) @@ -503,15 +521,13 @@ class VpnSettingsScreenTest { composeExtension.use { // Arrange val mockOnShowCustomPortDialog: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - selectedWireguardPort = Constraint.Only(Port(4000)) - ), - navigateToWireguardPortDialog = mockOnShowCustomPortDialog, - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + selectedWireguardPort = Constraint.Only(Port(4000)) + ), + navigateToWireguardPortDialog = mockOnShowCustomPortDialog, + ) // Act onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG) @@ -526,11 +542,7 @@ class VpnSettingsScreenTest { fun ensureConnectOnStartIsShownWhenSystemVpnSettingsAvailableIsFalse() = composeExtension.use { // Arrange - setContentWithTheme { - VpnSettingsScreen( - state = VpnSettingsUiState.createDefault(systemVpnSettingsAvailable = false) - ) - } + initScreen(state = VpnSettingsUiState.createDefault(systemVpnSettingsAvailable = false)) // Assert onNodeWithText("Connect on device start-up").assertExists() @@ -541,16 +553,14 @@ class VpnSettingsScreenTest { composeExtension.use { // Arrange val mockOnToggleAutoStartAndConnectOnBoot: (Boolean) -> Unit = mockk(relaxed = true) - setContentWithTheme { - VpnSettingsScreen( - state = - VpnSettingsUiState.createDefault( - systemVpnSettingsAvailable = false, - autoStartAndConnectOnBoot = false, - ), - onToggleAutoStartAndConnectOnBoot = mockOnToggleAutoStartAndConnectOnBoot, - ) - } + initScreen( + state = + VpnSettingsUiState.createDefault( + systemVpnSettingsAvailable = false, + autoStartAndConnectOnBoot = false, + ), + onToggleAutoStartAndConnectOnBoot = mockOnToggleAutoStartAndConnectOnBoot, + ) // Act onNodeWithText("Connect on device start-up").performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt index a9f1658308a3..8e9cc3fdf833 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/WelcomeScreenTest.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.mockk @@ -32,23 +33,37 @@ class WelcomeScreenTest { MockKAnnotations.init(this) } + private fun ComposeContext.initScreen( + state: WelcomeUiState = WelcomeUiState(), + onSitePaymentClick: () -> Unit = {}, + onRedeemVoucherClick: () -> Unit = {}, + onSettingsClick: () -> Unit = {}, + onAccountClick: () -> Unit = {}, + onPurchaseBillingProductClick: (productId: ProductId) -> Unit = {}, + onDisconnectClick: () -> Unit = {}, + navigateToDeviceInfoDialog: () -> Unit = {}, + navigateToVerificationPendingDialog: () -> Unit = {}, + ) { + setContentWithTheme { + WelcomeScreen( + state = state, + onSitePaymentClick = onSitePaymentClick, + onRedeemVoucherClick = onRedeemVoucherClick, + onSettingsClick = onSettingsClick, + onAccountClick = onAccountClick, + onPurchaseBillingProductClick = onPurchaseBillingProductClick, + navigateToDeviceInfoDialog = navigateToDeviceInfoDialog, + navigateToVerificationPendingDialog = navigateToVerificationPendingDialog, + onDisconnectClick = onDisconnectClick, + ) + } + } + @Test fun testDefaultState() = composeExtension.use { // Arrange - setContentWithTheme { - WelcomeScreen( - state = WelcomeUiState(), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {}, - onDisconnectClick = {}, - ) - } + initScreen() // Assert onNodeWithText("Congrats!").assertExists() @@ -59,19 +74,7 @@ class WelcomeScreenTest { fun testDisableSitePayment() = composeExtension.use { // Arrange - setContentWithTheme { - WelcomeScreen( - state = WelcomeUiState(), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {}, - onDisconnectClick = {}, - ) - } + initScreen() // Assert onNodeWithText( @@ -88,19 +91,7 @@ class WelcomeScreenTest { // Arrange val rawAccountNumber = AccountNumber("1111222233334444") val expectedAccountNumber = "1111 2222 3333 4444" - setContentWithTheme { - WelcomeScreen( - state = WelcomeUiState(accountNumber = rawAccountNumber), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {}, - onDisconnectClick = {}, - ) - } + initScreen(state = WelcomeUiState(accountNumber = rawAccountNumber)) // Assert onNodeWithText(expectedAccountNumber).assertExists() @@ -111,19 +102,10 @@ class WelcomeScreenTest { composeExtension.use { // Arrange val mockClickListener: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - WelcomeScreen( - state = WelcomeUiState(showSitePayment = true), - onSitePaymentClick = mockClickListener, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {}, - onDisconnectClick = {}, - ) - } + initScreen( + state = WelcomeUiState(showSitePayment = true), + onSitePaymentClick = mockClickListener, + ) // Act onNodeWithText("Buy credit").performClick() @@ -137,19 +119,7 @@ class WelcomeScreenTest { composeExtension.use { // Arrange val mockClickListener: () -> Unit = mockk(relaxed = true) - setContentWithTheme { - WelcomeScreen( - state = WelcomeUiState(), - onSitePaymentClick = {}, - onRedeemVoucherClick = mockClickListener, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {}, - onDisconnectClick = {}, - ) - } + initScreen(state = WelcomeUiState(), onRedeemVoucherClick = mockClickListener) // Act onNodeWithText("Redeem voucher").performClick() @@ -162,19 +132,9 @@ class WelcomeScreenTest { fun testShowBillingErrorPaymentButton() = composeExtension.use { // Arrange - setContentWithTheme { - WelcomeScreen( - state = WelcomeUiState().copy(billingPaymentState = PaymentState.Error.Billing), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {}, - onDisconnectClick = {}, - ) - } + initScreen( + state = WelcomeUiState().copy(billingPaymentState = PaymentState.Error.Billing) + ) // Assert onNodeWithText("Add 30 days time").assertExists() @@ -187,23 +147,13 @@ class WelcomeScreenTest { val mockPaymentProduct: PaymentProduct = mockk() every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns null - setContentWithTheme { - WelcomeScreen( - state = - WelcomeUiState( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {}, - onDisconnectClick = {}, - ) - } + initScreen( + state = + WelcomeUiState( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ) + ) // Assert onNodeWithText("Add 30 days time ($10)").assertExists() @@ -216,24 +166,13 @@ class WelcomeScreenTest { val mockPaymentProduct: PaymentProduct = mockk() every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns PaymentStatus.PENDING - setContentWithTheme { - WelcomeScreen( - state = - WelcomeUiState() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {}, - onDisconnectClick = {}, - ) - } + initScreen( + state = + WelcomeUiState( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ) + ) // Assert onNodeWithText("Google Play payment pending").assertExists() @@ -247,24 +186,14 @@ class WelcomeScreenTest { every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns PaymentStatus.PENDING val mockShowPendingInfo = mockk<() -> Unit>(relaxed = true) - setContentWithTheme { - WelcomeScreen( - state = - WelcomeUiState() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToVerificationPendingDialog = mockShowPendingInfo, - navigateToDeviceInfoDialog = {}, - onDisconnectClick = {}, - ) - } + initScreen( + state = + WelcomeUiState( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + navigateToVerificationPendingDialog = mockShowPendingInfo, + ) // Act onNodeWithTag(PLAY_PAYMENT_INFO_ICON_TEST_TAG).performClick() @@ -280,24 +209,14 @@ class WelcomeScreenTest { val mockPaymentProduct: PaymentProduct = mockk() every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.status } returns PaymentStatus.VERIFICATION_IN_PROGRESS - setContentWithTheme { - WelcomeScreen( - state = - WelcomeUiState() - .copy( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = { _ -> }, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {}, - onDisconnectClick = {}, - ) - } + + initScreen( + state = + WelcomeUiState( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ) + ) // Assert onNodeWithText("Verifying purchase").assertExists() @@ -312,23 +231,14 @@ class WelcomeScreenTest { every { mockPaymentProduct.price } returns ProductPrice("$10") every { mockPaymentProduct.productId } returns ProductId("PRODUCT_ID") every { mockPaymentProduct.status } returns null - setContentWithTheme { - WelcomeScreen( - state = - WelcomeUiState( - billingPaymentState = - PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) - ), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = clickHandler, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {}, - onDisconnectClick = {}, - ) - } + initScreen( + state = + WelcomeUiState( + billingPaymentState = + PaymentState.PaymentAvailable(listOf(mockPaymentProduct)) + ), + onPurchaseBillingProductClick = clickHandler, + ) // Act onNodeWithText("Add 30 days time ($10)").performClick() @@ -344,19 +254,10 @@ class WelcomeScreenTest { val clickHandler: () -> Unit = mockk(relaxed = true) val tunnelState: TunnelState = mockk(relaxed = true) every { tunnelState.isSecured() } returns true - setContentWithTheme { - WelcomeScreen( - state = WelcomeUiState(tunnelState = tunnelState), - onSitePaymentClick = {}, - onRedeemVoucherClick = {}, - onSettingsClick = {}, - onAccountClick = {}, - onPurchaseBillingProductClick = {}, - navigateToDeviceInfoDialog = {}, - navigateToVerificationPendingDialog = {}, - onDisconnectClick = clickHandler, - ) - } + initScreen( + state = WelcomeUiState(tunnelState = tunnelState), + onDisconnectClick = clickHandler, + ) // Act onNodeWithText("Disconnect").performClick() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreenTest.kt index 5901599df9c4..8e87c03e13b1 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreenTest.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performTextInput +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.mockk import io.mockk.unmockkAll @@ -14,6 +15,9 @@ import net.mullvad.mullvadvpn.compose.setContentWithTheme import net.mullvad.mullvadvpn.compose.state.RelayListItem import net.mullvad.mullvadvpn.compose.state.SearchLocationUiState import net.mullvad.mullvadvpn.compose.test.SELECT_LOCATION_CUSTOM_LIST_HEADER_TEST_TAG +import net.mullvad.mullvadvpn.lib.model.CustomListId +import net.mullvad.mullvadvpn.lib.model.RelayItem +import net.mullvad.mullvadvpn.lib.model.RelayItemId import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -33,18 +37,57 @@ class SearchLocationScreenTest { unmockkAll() } + private fun ComposeContext.initScreen( + state: SearchLocationUiState, + onSelectRelay: (RelayItem) -> Unit = {}, + onToggleExpand: (RelayItemId, CustomListId?, Boolean) -> Unit = { _, _, _ -> }, + onSearchInputChanged: (String) -> Unit = {}, + onCreateCustomList: (location: RelayItem.Location?) -> Unit = {}, + onEditCustomLists: () -> Unit = {}, + onAddLocationToList: + (location: RelayItem.Location, customList: RelayItem.CustomList) -> Unit = + { _, _ -> + }, + onRemoveLocationFromList: + (location: RelayItem.Location, customListId: CustomListId) -> Unit = + { _, _ -> + }, + onEditCustomListName: (RelayItem.CustomList) -> Unit = {}, + onEditLocationsCustomList: (RelayItem.CustomList) -> Unit = {}, + onDeleteCustomList: (RelayItem.CustomList) -> Unit = {}, + onRemoveOwnershipFilter: () -> Unit = {}, + onRemoveProviderFilter: () -> Unit = {}, + onGoBack: () -> Unit = {}, + ) { + setContentWithTheme { + SearchLocationScreen( + state = state, + onSelectRelay = onSelectRelay, + onToggleExpand = onToggleExpand, + onSearchInputChanged = onSearchInputChanged, + onCreateCustomList = onCreateCustomList, + onEditCustomLists = onEditCustomLists, + onAddLocationToList = onAddLocationToList, + onRemoveLocationFromList = onRemoveLocationFromList, + onEditCustomListName = onEditCustomListName, + onEditLocationsCustomList = onEditLocationsCustomList, + onDeleteCustomList = onDeleteCustomList, + onRemoveOwnershipFilter = onRemoveOwnershipFilter, + onRemoveProviderFilter = onRemoveProviderFilter, + onGoBack = onGoBack, + ) + } + } + @Test fun testSearchInput() = composeExtension.use { // Arrange val mockedSearchTermInput: (String) -> Unit = mockk(relaxed = true) - setContentWithTheme { - SearchLocationScreen( - state = - SearchLocationUiState.NoQuery(searchTerm = "", filterChips = emptyList()), - onSearchInputChanged = mockedSearchTermInput, - ) - } + initScreen( + state = SearchLocationUiState.NoQuery(searchTerm = "", filterChips = emptyList()), + onSearchInputChanged = mockedSearchTermInput, + ) val mockSearchString = "SEARCH" // Act @@ -59,18 +102,15 @@ class SearchLocationScreenTest { composeExtension.use { // Arrange val mockSearchString = "SEARCH" - setContentWithTheme { - SearchLocationScreen( - state = - SearchLocationUiState.Content( - searchTerm = mockSearchString, - filterChips = emptyList(), - relayListItems = - listOf(RelayListItem.LocationsEmptyText(mockSearchString)), - customLists = emptyList(), - ) - ) - } + initScreen( + state = + SearchLocationUiState.Content( + searchTerm = mockSearchString, + filterChips = emptyList(), + relayListItems = listOf(RelayListItem.LocationsEmptyText(mockSearchString)), + customLists = emptyList(), + ) + ) // Assert onNodeWithText("No result for \"$mockSearchString\", please try a different search") @@ -82,17 +122,15 @@ class SearchLocationScreenTest { composeExtension.use { // Arrange val mockSearchString = "SEARCH" - setContentWithTheme { - SearchLocationScreen( - state = - SearchLocationUiState.Content( - searchTerm = mockSearchString, - filterChips = emptyList(), - relayListItems = emptyList(), - customLists = DUMMY_RELAY_ITEM_CUSTOM_LISTS, - ) - ) - } + initScreen( + state = + SearchLocationUiState.Content( + searchTerm = mockSearchString, + filterChips = emptyList(), + relayListItems = emptyList(), + customLists = DUMMY_RELAY_ITEM_CUSTOM_LISTS, + ) + ) // Assert onNodeWithText(CUSTOM_LISTS_EMPTY_TEXT).assertDoesNotExist() diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreenTest.kt index cf3380e97a42..d0dbb9155d51 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreenTest.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import de.mannodermaus.junit5.compose.ComposeContext import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.mockk @@ -20,6 +21,7 @@ import net.mullvad.mullvadvpn.compose.state.SelectLocationListUiState import net.mullvad.mullvadvpn.compose.state.SelectLocationUiState import net.mullvad.mullvadvpn.compose.test.SELECT_LOCATION_CUSTOM_LIST_BOTTOM_SHEET_TEST_TAG import net.mullvad.mullvadvpn.compose.test.SELECT_LOCATION_LOCATION_BOTTOM_SHEET_TEST_TAG +import net.mullvad.mullvadvpn.lib.model.CustomListId import net.mullvad.mullvadvpn.lib.model.RelayItem import net.mullvad.mullvadvpn.performLongClick import net.mullvad.mullvadvpn.viewmodel.location.SelectLocationListViewModel @@ -49,6 +51,53 @@ class SelectLocationScreenTest { unmockkAll() } + private fun ComposeContext.initScreen( + state: SelectLocationUiState = SelectLocationUiState.Loading, + onSelectRelay: (item: RelayItem) -> Unit = {}, + onSearchClick: (RelayListType) -> Unit = {}, + onBackClick: () -> Unit = {}, + onFilterClick: () -> Unit = {}, + onCreateCustomList: (location: RelayItem.Location?) -> Unit = {}, + onEditCustomLists: () -> Unit = {}, + removeOwnershipFilter: () -> Unit = {}, + removeProviderFilter: () -> Unit = {}, + onAddLocationToList: + (location: RelayItem.Location, customList: RelayItem.CustomList) -> Unit = + { _, _ -> + }, + onRemoveLocationFromList: + (location: RelayItem.Location, customListId: CustomListId) -> Unit = + { _, _ -> + }, + onEditCustomListName: (RelayItem.CustomList) -> Unit = {}, + onEditLocationsCustomList: (RelayItem.CustomList) -> Unit = {}, + onDeleteCustomList: (RelayItem.CustomList) -> Unit = {}, + onSelectRelayList: (RelayListType) -> Unit = {}, + openDaitaSettings: () -> Unit = {}, + ) { + + setContentWithTheme { + SelectLocationScreen( + state = state, + onSelectRelay = onSelectRelay, + onSearchClick = onSearchClick, + onBackClick = onBackClick, + onFilterClick = onFilterClick, + onCreateCustomList = onCreateCustomList, + onEditCustomLists = onEditCustomLists, + removeOwnershipFilter = removeOwnershipFilter, + removeProviderFilter = removeProviderFilter, + onAddLocationToList = onAddLocationToList, + onRemoveLocationFromList = onRemoveLocationFromList, + onEditCustomListName = onEditCustomListName, + onEditLocationsCustomList = onEditLocationsCustomList, + onDeleteCustomList = onDeleteCustomList, + onSelectRelayList = onSelectRelayList, + openDaitaSettings = openDaitaSettings, + ) + } + } + @Test fun testShowRelayListState() = composeExtension.use { @@ -61,17 +110,15 @@ class SelectLocationScreenTest { customLists = emptyList(), ) ) - setContentWithTheme { - SelectLocationScreen( - state = - SelectLocationUiState.Data( - // searchTerm = "", - filterChips = emptyList(), - multihopEnabled = false, - relayListType = RelayListType.EXIT, - ) - ) - } + initScreen( + state = + SelectLocationUiState.Data( + // searchTerm = "", + filterChips = emptyList(), + multihopEnabled = false, + relayListType = RelayListType.EXIT, + ) + ) // Assert onNodeWithText("Relay Country 1").assertExists() @@ -93,16 +140,14 @@ class SelectLocationScreenTest { customLists = emptyList(), ) ) - setContentWithTheme { - SelectLocationScreen( - state = - SelectLocationUiState.Data( - filterChips = emptyList(), - multihopEnabled = false, - relayListType = RelayListType.EXIT, - ) - ) - } + initScreen( + state = + SelectLocationUiState.Data( + filterChips = emptyList(), + multihopEnabled = false, + relayListType = RelayListType.EXIT, + ) + ) // Assert onNodeWithText(CUSTOM_LISTS_EMPTY_TEXT).assertExists() @@ -121,17 +166,15 @@ class SelectLocationScreenTest { ) ) val mockedOnSelectRelay: (RelayItem) -> Unit = mockk(relaxed = true) - setContentWithTheme { - SelectLocationScreen( - state = - SelectLocationUiState.Data( - filterChips = emptyList(), - multihopEnabled = false, - relayListType = RelayListType.EXIT, - ), - onSelectRelay = mockedOnSelectRelay, - ) - } + initScreen( + state = + SelectLocationUiState.Data( + filterChips = emptyList(), + multihopEnabled = false, + relayListType = RelayListType.EXIT, + ), + onSelectRelay = mockedOnSelectRelay, + ) // Act onNodeWithText(customList.name).performClick() @@ -153,18 +196,16 @@ class SelectLocationScreenTest { ) ) val mockedOnSelectRelay: (RelayItem) -> Unit = mockk(relaxed = true) - setContentWithTheme { - SelectLocationScreen( - state = - SelectLocationUiState.Data( - // searchTerm = "", - filterChips = emptyList(), - multihopEnabled = false, - relayListType = RelayListType.EXIT, - ), - onSelectRelay = mockedOnSelectRelay, - ) - } + initScreen( + state = + SelectLocationUiState.Data( + // searchTerm = "", + filterChips = emptyList(), + multihopEnabled = false, + relayListType = RelayListType.EXIT, + ), + onSelectRelay = mockedOnSelectRelay, + ) // Act onNodeWithText(customList.name).performLongClick() @@ -186,17 +227,15 @@ class SelectLocationScreenTest { ) ) val mockedOnSelectRelay: (RelayItem) -> Unit = mockk(relaxed = true) - setContentWithTheme { - SelectLocationScreen( - state = - SelectLocationUiState.Data( - filterChips = emptyList(), - multihopEnabled = false, - relayListType = RelayListType.EXIT, - ), - onSelectRelay = mockedOnSelectRelay, - ) - } + initScreen( + state = + SelectLocationUiState.Data( + filterChips = emptyList(), + multihopEnabled = false, + relayListType = RelayListType.EXIT, + ), + onSelectRelay = mockedOnSelectRelay, + ) // Act onNodeWithText(relayItem.name).performLongClick() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt index 47690247dc5a..8c336e554440 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt @@ -31,9 +31,9 @@ private fun PreviewConnectionButton( fun ConnectionButton( modifier: Modifier = Modifier, state: TunnelState, - disconnectClick: () -> Unit = {}, - cancelClick: () -> Unit = {}, - connectClick: () -> Unit = {}, + disconnectClick: () -> Unit, + cancelClick: () -> Unit, + connectClick: () -> Unit, ) { val containerColor = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomPortCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomPortCell.kt index 321a86700fa4..0ea351e8b14a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomPortCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomPortCell.kt @@ -40,8 +40,20 @@ import net.mullvad.mullvadvpn.lib.theme.color.selected private fun PreviewCustomPortCell() { AppTheme { SpacedColumn(Modifier.background(MaterialTheme.colorScheme.surface)) { - CustomPortCell(title = "Title", isSelected = true, port = Port(444)) - CustomPortCell(title = "Title", isSelected = false, port = null) + CustomPortCell( + title = "Title", + isSelected = true, + port = Port(444), + onPortCellClicked = {}, + onMainCellClicked = {}, + ) + CustomPortCell( + title = "Title", + isSelected = false, + port = null, + onPortCellClicked = {}, + onMainCellClicked = {}, + ) } } } @@ -53,8 +65,8 @@ fun CustomPortCell( port: Port?, mainTestTag: String = "", numberTestTag: String = "", - onMainCellClicked: () -> Unit = {}, - onPortCellClicked: () -> Unit = {}, + onMainCellClicked: () -> Unit, + onPortCellClicked: () -> Unit, ) { Row( verticalAlignment = Alignment.CenterVertically, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DropdownMenuCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DropdownMenuCell.kt index 5560a6975477..70b04822eea4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DropdownMenuCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DropdownMenuCell.kt @@ -18,14 +18,14 @@ import net.mullvad.mullvadvpn.lib.theme.Dimens @Preview @Composable private fun PreviewThreeDotCell() { - AppTheme { ThreeDotCell(text = "Three dots") } + AppTheme { ThreeDotCell(text = "Three dots", onClickDots = {}) } } @Composable fun ThreeDotCell( text: String, modifier: Modifier = Modifier, - onClickDots: () -> Unit = {}, + onClickDots: () -> Unit, textStyle: TextStyle = MaterialTheme.typography.titleMedium, textColor: Color = MaterialTheme.colorScheme.onPrimary, background: Color = MaterialTheme.colorScheme.primary, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt index 81a5bd0ea5b2..a1ca4333227f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt @@ -53,7 +53,7 @@ fun ExpandableComposeCell( testTag: String = "", textColor: Color = MaterialTheme.colorScheme.onPrimary, background: Color = MaterialTheme.colorScheme.primary, - onCellClicked: (Boolean) -> Unit = {}, + onCellClicked: (Boolean) -> Unit, onInfoClicked: (() -> Unit)? = null, ) { val titleModifier = Modifier.alpha(if (isEnabled) AlphaVisible else AlphaInactive) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ObfuscationModeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ObfuscationModeCell.kt index 5191eba331b4..90b5da88912b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ObfuscationModeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ObfuscationModeCell.kt @@ -57,7 +57,7 @@ fun ObfuscationModeCell( port: Constraint, isSelected: Boolean, onSelected: (ObfuscationMode) -> Unit, - onNavigate: () -> Unit = {}, + onNavigate: () -> Unit, testTag: String? = null, ) { Row( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt index eb729701bc9d..14d9c20ebeba 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/RelayLocationCell.kt @@ -75,9 +75,9 @@ fun StatusRelayItemCell( isSelected: Boolean, state: RelayListItemState?, modifier: Modifier = Modifier, - onClick: () -> Unit = {}, + onClick: () -> Unit, onLongClick: (() -> Unit)? = null, - onToggleExpand: ((Boolean) -> Unit) = {}, + onToggleExpand: ((Boolean) -> Unit), isExpanded: Boolean = false, depth: Int = 0, activeColor: Color = MaterialTheme.colorScheme.selected, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SplitTunnelingCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SplitTunnelingCell.kt index 05bbcb1b5aa2..15a83776d4e9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SplitTunnelingCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SplitTunnelingCell.kt @@ -48,12 +48,16 @@ private fun PreviewTunnelingCell() { packageName = "", isSelected = false, enabled = true, + onCellClicked = {}, + onResolveIcon = { null }, ) SplitTunnelingCell( title = "Mullvad VPN", packageName = "", isSelected = true, enabled = true, + onCellClicked = {}, + onResolveIcon = { null }, ) } } @@ -67,8 +71,8 @@ fun SplitTunnelingCell( enabled: Boolean, modifier: Modifier = Modifier, backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerHigh, - onResolveIcon: (String) -> Bitmap? = { null }, - onCellClicked: () -> Unit = {}, + onResolveIcon: (String) -> Bitmap?, + onCellClicked: () -> Unit, ) { var icon by remember(packageName) { mutableStateOf(null) } LaunchedEffect(packageName) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt index 3f0599573841..61b9a828a5d5 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt @@ -64,7 +64,7 @@ fun NormalSwitchComposeCell( isEnabled: Boolean = true, background: Color = MaterialTheme.colorScheme.surfaceContainerLow, onBackground: Color = MaterialTheme.colorScheme.onSurface, - onCellClicked: (Boolean) -> Unit = {}, + onCellClicked: (Boolean) -> Unit, onInfoClicked: (() -> Unit)? = null, ) { SwitchComposeCell( @@ -95,7 +95,7 @@ fun HeaderSwitchComposeCell( isEnabled: Boolean = true, background: Color = MaterialTheme.colorScheme.primary, onBackground: Color = MaterialTheme.colorScheme.onPrimary, - onCellClicked: (Boolean) -> Unit = {}, + onCellClicked: (Boolean) -> Unit, onInfoClicked: (() -> Unit)? = null, ) { SwitchComposeCell( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ChangelogDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ChangelogDialog.kt index 67a6aac2fefc..e5afe6d9bc96 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ChangelogDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ChangelogDialog.kt @@ -39,12 +39,12 @@ fun Changelog(navController: NavController) { } @Composable -fun ChangelogDialog(changeLog: ChangelogUiState, onDismiss: () -> Unit) { +fun ChangelogDialog(state: ChangelogUiState, onDismiss: () -> Unit) { AlertDialog( onDismissRequest = onDismiss, title = { Text( - text = changeLog.version, + text = state.version, style = MaterialTheme.typography.headlineLarge, color = MaterialTheme.colorScheme.onSurface, textAlign = TextAlign.Center, @@ -64,7 +64,7 @@ fun ChangelogDialog(changeLog: ChangelogUiState, onDismiss: () -> Unit) { modifier = Modifier.fillMaxWidth(), ) - changeLog.changes.forEach { changeItem -> ChangeListItem(text = changeItem) } + state.changes.forEach { changeItem -> ChangeListItem(text = changeItem) } } }, confirmButton = { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialog.kt index fc9109cf4d10..b93c08321ce7 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CreateCustomListDialog.kt @@ -33,7 +33,7 @@ import org.koin.androidx.compose.koinViewModel @Preview @Composable private fun PreviewCreateCustomListDialog() { - AppTheme { CreateCustomListDialog(state = CreateCustomListUiState()) } + AppTheme { CreateCustomListDialog(state = CreateCustomListUiState(), {}, {}, {}) } } @Preview @@ -44,7 +44,10 @@ private fun PreviewCreateCustomListDialogError() { state = CreateCustomListUiState( error = CreateWithLocationsError.Create(CustomListAlreadyExists) - ) + ), + {}, + {}, + {}, ) } } @@ -92,9 +95,9 @@ fun CreateCustomList( @Composable fun CreateCustomListDialog( state: CreateCustomListUiState, - createCustomList: (String) -> Unit = {}, - onInputChanged: () -> Unit = {}, - onDismiss: () -> Unit = {}, + createCustomList: (String) -> Unit, + onInputChanged: () -> Unit, + onDismiss: () -> Unit, ) { val name = rememberSaveable { mutableStateOf("") } val isValidName = name.value.isNotBlank() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialog.kt index 3153aeb37c44..1dffa3bfc950 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeleteCustomListConfirmationDialog.kt @@ -26,7 +26,9 @@ import org.koin.androidx.compose.koinViewModel private fun PreviewRemoveDeviceConfirmationDialog() { AppTheme { DeleteCustomListConfirmationDialog( - state = DeleteCustomListUiState(CustomListName.fromString("My Custom List"), null) + state = DeleteCustomListUiState(CustomListName.fromString("My Custom List"), null), + {}, + {}, ) } } @@ -59,8 +61,8 @@ fun DeleteCustomList(navigator: ResultBackNavigator Unit = {}, - onBack: () -> Unit = {}, + onDelete: () -> Unit, + onBack: () -> Unit, ) { NegativeConfirmationDialog( onConfirm = onDelete, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialog.kt index 57205175e5c4..20560fe9e87d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/EditCustomListNameDialog.kt @@ -32,7 +32,7 @@ import org.koin.androidx.compose.koinViewModel @Preview @Composable private fun PreviewEditCustomListNameDialog() { - AppTheme { EditCustomListNameDialog(EditCustomListNameUiState()) } + AppTheme { EditCustomListNameDialog(EditCustomListNameUiState(), {}, {}, {}) } } data class EditCustomListNameNavArgs( @@ -69,9 +69,9 @@ fun EditCustomListName( @Composable fun EditCustomListNameDialog( state: EditCustomListNameUiState, - updateName: (String) -> Unit = {}, - onInputChanged: (String) -> Unit = {}, - onDismiss: () -> Unit = {}, + updateName: (String) -> Unit, + onInputChanged: (String) -> Unit, + onDismiss: () -> Unit, ) { InputDialog( title = stringResource(id = R.string.update_list_name), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/NegativeConfirmationDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/NegativeConfirmationDialog.kt index 4720fd9fb72a..dab6142c896d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/NegativeConfirmationDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/NegativeConfirmationDialog.kt @@ -30,7 +30,12 @@ import net.mullvad.mullvadvpn.lib.theme.Dimens @Composable private fun PreviewDeleteConfirmationDialog() { AppTheme { - NegativeConfirmationDialog(message = "Do you want to delete Cookie?", errorMessage = null) + NegativeConfirmationDialog( + message = "Do you want to delete Cookie?", + errorMessage = null, + onConfirm = {}, + onBack = {}, + ) } } @@ -41,6 +46,8 @@ private fun PreviewDeleteConfirmationDialogError() { NegativeConfirmationDialog( message = "Do you want to delete Cookie?", errorMessage = "An error occured", + onConfirm = {}, + onBack = {}, ) } } @@ -52,8 +59,8 @@ fun NegativeConfirmationDialog( errorMessage: String? = null, confirmationText: String = stringResource(id = R.string.delete), cancelText: String = stringResource(id = R.string.cancel), - onConfirm: () -> Unit = {}, - onBack: () -> Unit = {}, + onConfirm: () -> Unit, + onBack: () -> Unit, ) { NegativeConfirmationDialog( message = AnnotatedString(message), @@ -73,8 +80,8 @@ fun NegativeConfirmationDialog( errorMessage: String? = null, confirmationText: String = stringResource(id = R.string.delete), cancelText: String = stringResource(id = R.string.cancel), - onConfirm: () -> Unit = {}, - onBack: () -> Unit = {}, + onConfirm: () -> Unit, + onBack: () -> Unit, ) { AlertDialog( onDismissRequest = onBack, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt index 04f3c05a11e0..5f8968beec39 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt @@ -121,7 +121,7 @@ fun RedeemVoucher(resultBackNavigator: ResultBackNavigator) { @Composable fun RedeemVoucherDialog( state: VoucherDialogUiState, - onVoucherInputChange: (String) -> Unit = {}, + onVoucherInputChange: (String) -> Unit, onRedeem: (voucherCode: String) -> Unit, onDismiss: (isTimeAdded: Boolean) -> Unit, ) { @@ -230,7 +230,7 @@ private fun RedeemSuccessBody(message: String) { @Composable private fun EnterVoucherBody( state: VoucherDialogUiState, - onVoucherInputChange: (String) -> Unit = {}, + onVoucherInputChange: (String) -> Unit, onRedeem: (voucherCode: String) -> Unit, ) { CustomTextField( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialog.kt index 6182f0eb8194..f351c95c0e9c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/SaveApiAccessMethodDialog.kt @@ -42,7 +42,7 @@ private fun PreviewSaveApiAccessMethodDialog( @PreviewParameter(SaveApiAccessMethodUiStatePreviewParameterProvider::class) state: SaveApiAccessMethodUiState ) { - AppTheme { SaveApiAccessMethodDialog(state = state) } + AppTheme { SaveApiAccessMethodDialog(state = state, {}, {}) } } data class SaveApiAccessMethodNavArgs( @@ -79,8 +79,8 @@ fun SaveApiAccessMethod(backNavigator: ResultBackNavigator) { @Composable fun SaveApiAccessMethodDialog( state: SaveApiAccessMethodUiState, - onCancel: () -> Unit = {}, - onSave: () -> Unit = {}, + onCancel: () -> Unit, + onSave: () -> Unit, ) { AlertDialog( icon = { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/payment/PaymentDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/payment/PaymentDialog.kt index 1001490339e1..72fdd752e90f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/payment/PaymentDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/payment/PaymentDialog.kt @@ -147,8 +147,8 @@ fun Payment(productId: ProductId, resultBackNavigator: ResultBackNavigator Unit = {}, - onCloseDialog: (isPaymentSuccessful: Boolean) -> Unit = {}, + retryPurchase: (ProductId) -> Unit, + onCloseDialog: (isPaymentSuccessful: Boolean) -> Unit, ) { val clickResolver: (action: PaymentDialogAction) -> Unit = { when (it) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt index 3e95235d235b..ddfb535fafc8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt @@ -72,7 +72,7 @@ import org.koin.androidx.compose.koinViewModel private fun PreviewAccountScreen( @PreviewParameter(AccountUiStatePreviewParameterProvider::class) state: AccountUiState ) { - AppTheme { AccountScreen(state = state) } + AppTheme { AccountScreen(state = state, SnackbarHostState(), {}, {}, {}, {}, {}, {}, {}, {}) } } @OptIn(ExperimentalMaterial3Api::class) @@ -138,14 +138,14 @@ fun Account( fun AccountScreen( state: AccountUiState, snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, - onCopyAccountNumber: (String) -> Unit = {}, - onRedeemVoucherClick: () -> Unit = {}, - onManageAccountClick: () -> Unit = {}, - onLogoutClick: () -> Unit = {}, - onPurchaseBillingProductClick: (productId: ProductId) -> Unit = { _ -> }, - navigateToDeviceInfo: () -> Unit = {}, - navigateToVerificationPendingDialog: () -> Unit = {}, - onBackClick: () -> Unit = {}, + onCopyAccountNumber: (String) -> Unit, + onRedeemVoucherClick: () -> Unit, + onManageAccountClick: () -> Unit, + onLogoutClick: () -> Unit, + onPurchaseBillingProductClick: (productId: ProductId) -> Unit, + navigateToDeviceInfo: () -> Unit, + navigateToVerificationPendingDialog: () -> Unit, + onBackClick: () -> Unit, ) { // This will enable SECURE_FLAG while this screen is visible to preview screenshot SecureScreenWhileInView() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessListScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessListScreen.kt index c1fc183cfa1c..fb5f03015cd2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessListScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessListScreen.kt @@ -51,7 +51,7 @@ private fun PreviewApiAccessList( @PreviewParameter(ApiAccessListUiStatePreviewParameterProvider::class) state: ApiAccessListUiState ) { - AppTheme { ApiAccessListScreen(state = state) } + AppTheme { ApiAccessListScreen(state = state, {}, { _ -> }, {}, {}) } } @Destination(style = SlideInFromRightTransition::class) @@ -78,10 +78,10 @@ fun ApiAccessList(navigator: DestinationsNavigator) { @Composable fun ApiAccessListScreen( state: ApiAccessListUiState, - onAddMethodClick: () -> Unit = {}, - onApiAccessMethodClick: (apiAccessMethodSetting: ApiAccessMethodSetting) -> Unit = {}, - onApiAccessInfoClick: () -> Unit = {}, - onBackClick: () -> Unit = {}, + onAddMethodClick: () -> Unit, + onApiAccessMethodClick: (apiAccessMethodSetting: ApiAccessMethodSetting) -> Unit, + onApiAccessInfoClick: () -> Unit, + onBackClick: () -> Unit, ) { ScaffoldWithMediumTopBar( appBarTitle = stringResource(id = R.string.settings_api_access), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreen.kt index 35c4c8a444ff..b4f10d691ba3 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ApiAccessMethodDetailsScreen.kt @@ -77,7 +77,9 @@ private fun PreviewApiAccessMethodDetailsScreen( @PreviewParameter(ApiAccessMethodDetailsUiStatePreviewParameterProvider::class) state: ApiAccessMethodDetailsUiState ) { - AppTheme { ApiAccessMethodDetailsScreen(state = state) } + AppTheme { + ApiAccessMethodDetailsScreen(state = state, SnackbarHostState(), {}, {}, {}, {}, {}, {}, {}) + } } data class ApiAccessMethodDetailsNavArgs(val accessMethodId: ApiAccessMethodId) @@ -187,14 +189,14 @@ fun ApiAccessMethodDetails( @Composable fun ApiAccessMethodDetailsScreen( state: ApiAccessMethodDetailsUiState, - snackbarHostState: SnackbarHostState = SnackbarHostState(), - onEditMethodClicked: () -> Unit = {}, - onEnableClicked: (Boolean) -> Unit = {}, - onTestMethodClicked: () -> Unit = {}, - onUseMethodClicked: () -> Unit = {}, - onDeleteApiAccessMethodClicked: (ApiAccessMethodId) -> Unit = {}, - onNavigateToEncryptedDnsInfoDialog: () -> Unit = {}, - onBackClicked: () -> Unit = {}, + snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, + onEditMethodClicked: () -> Unit, + onEnableClicked: (Boolean) -> Unit, + onTestMethodClicked: () -> Unit, + onUseMethodClicked: () -> Unit, + onDeleteApiAccessMethodClicked: (ApiAccessMethodId) -> Unit, + onNavigateToEncryptedDnsInfoDialog: () -> Unit, + onBackClicked: () -> Unit, ) { ScaffoldWithMediumTopBar( appBarTitle = state.name(), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AutoConnectAndLockdownModeScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AutoConnectAndLockdownModeScreen.kt index cafb9ef11d92..3d2949fc9c8e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AutoConnectAndLockdownModeScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AutoConnectAndLockdownModeScreen.kt @@ -67,7 +67,7 @@ import net.mullvad.mullvadvpn.util.appendHideNavOnPlayBuild @Preview @Composable private fun PreviewAutoConnectAndLockdownModeScreen() { - AppTheme { AutoConnectAndLockdownModeScreen() } + AppTheme { AutoConnectAndLockdownModeScreen({}) } } @Destination(style = SlideInFromRightTransition::class) @@ -77,7 +77,7 @@ fun AutoConnectAndLockdownMode(navigator: DestinationsNavigator) { } @Composable -fun AutoConnectAndLockdownModeScreen(onBackClick: () -> Unit = {}) { +fun AutoConnectAndLockdownModeScreen(onBackClick: () -> Unit) { val context = LocalContext.current ScaffoldWithLargeTopBarAndButton( appBarTitle = stringResource(id = R.string.auto_connect_and_lockdown_mode), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt index b143e72f25c3..30af30fec518 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt @@ -125,7 +125,22 @@ private const val TALL_SCREEN_INDICATOR_BIAS = 0.3f private fun PreviewAccountScreen( @PreviewParameter(ConnectUiStatePreviewParameterProvider::class) state: ConnectUiState ) { - AppTheme { ConnectScreen(state = state) } + AppTheme { + ConnectScreen( + state = state, + snackbarHostState = SnackbarHostState(), + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + ) + } } @Destination(style = HomeTransition::class) @@ -228,17 +243,17 @@ fun Connect( @Composable fun ConnectScreen( state: ConnectUiState, - snackbarHostState: SnackbarHostState = SnackbarHostState(), - onDisconnectClick: () -> Unit = {}, - onReconnectClick: () -> Unit = {}, - onConnectClick: () -> Unit = {}, - onCancelClick: () -> Unit = {}, - onSwitchLocationClick: () -> Unit = {}, - onOpenAppListing: () -> Unit = {}, - onManageAccountClick: () -> Unit = {}, - onSettingsClick: () -> Unit = {}, - onAccountClick: () -> Unit = {}, - onDismissNewDeviceClick: () -> Unit = {}, + snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, + onDisconnectClick: () -> Unit, + onReconnectClick: () -> Unit, + onConnectClick: () -> Unit, + onCancelClick: () -> Unit, + onSwitchLocationClick: () -> Unit, + onOpenAppListing: () -> Unit, + onManageAccountClick: () -> Unit, + onSettingsClick: () -> Unit, + onAccountClick: () -> Unit, + onDismissNewDeviceClick: () -> Unit, ) { ScaffoldWithTopBarAndDeviceName( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreen.kt index 8fcf650bf43b..16234326d299 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListLocationsScreen.kt @@ -63,7 +63,7 @@ private fun PreviewCustomListLocationScreen( @PreviewParameter(CustomListLocationUiStatePreviewParameterProvider::class) state: CustomListLocationsUiState ) { - AppTheme { CustomListLocationsScreen(state = state) } + AppTheme { CustomListLocationsScreen(state = state, {}, {}, { _, _ -> }, { _, _ -> }, {}) } } data class CustomListLocationsNavArgs(val customListId: CustomListId, val newList: Boolean) @@ -119,11 +119,11 @@ fun CustomListLocations( @Composable fun CustomListLocationsScreen( state: CustomListLocationsUiState, - onSearchTermInput: (String) -> Unit = {}, - onSaveClick: () -> Unit = {}, - onRelaySelectionClick: (RelayItem.Location, selected: Boolean) -> Unit = { _, _ -> }, - onExpand: (RelayItem.Location, selected: Boolean) -> Unit = { _, _ -> }, - onBackClick: () -> Unit = {}, + onSearchTermInput: (String) -> Unit, + onSaveClick: () -> Unit, + onRelaySelectionClick: (RelayItem.Location, selected: Boolean) -> Unit, + onExpand: (RelayItem.Location, selected: Boolean) -> Unit, + onBackClick: () -> Unit, ) { ScaffoldWithSmallTopBar( appBarTitle = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreen.kt index 08ec0ee503f3..0d2a47e02668 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/CustomListsScreen.kt @@ -60,7 +60,7 @@ import org.koin.androidx.compose.koinViewModel private fun PreviewAccountScreen( @PreviewParameter(CustomListsUiStatePreviewParameterProvider::class) state: CustomListsUiState ) { - AppTheme { CustomListsScreen(state = state, SnackbarHostState()) } + AppTheme { CustomListsScreen(state = state, SnackbarHostState(), {}, { _ -> }, {}) } } @Composable @@ -112,10 +112,10 @@ fun CustomLists( @Composable fun CustomListsScreen( state: CustomListsUiState, - snackbarHostState: SnackbarHostState, - addCustomList: () -> Unit = {}, - openCustomList: (CustomList) -> Unit = {}, - onBackClick: () -> Unit = {}, + snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, + addCustomList: () -> Unit, + openCustomList: (CustomList) -> Unit, + onBackClick: () -> Unit, ) { ScaffoldWithMediumTopBar( appBarTitle = stringResource(id = R.string.edit_custom_lists), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DaitaScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DaitaScreen.kt index a6a2a279dc93..70e0d1622a9c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DaitaScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DaitaScreen.kt @@ -50,7 +50,15 @@ import org.koin.androidx.compose.koinViewModel @Preview @Composable private fun PreviewDaitaScreen() { - AppTheme { DaitaScreen(state = DaitaUiState(daitaEnabled = false, directOnly = false)) } + AppTheme { + DaitaScreen( + state = DaitaUiState(daitaEnabled = false, directOnly = false), + { _ -> }, + { _ -> }, + {}, + {}, + ) + } } @Destination(style = SlideInFromRightTransition::class) @@ -87,10 +95,10 @@ fun Daita( @Composable fun DaitaScreen( state: DaitaUiState, - onDaitaEnabled: (enable: Boolean) -> Unit = {}, - onDirectOnlyClick: (enable: Boolean) -> Unit = {}, - onDirectOnlyInfoClick: () -> Unit = {}, - onBackClick: () -> Unit = {}, + onDaitaEnabled: (enable: Boolean) -> Unit, + onDirectOnlyClick: (enable: Boolean) -> Unit, + onDirectOnlyInfoClick: () -> Unit, + onBackClick: () -> Unit, ) { ScaffoldWithMediumTopBar( appBarTitle = stringResource(id = R.string.daita), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt index beb83a9b2efc..34ac8d98b7fc 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt @@ -74,7 +74,7 @@ import org.koin.androidx.compose.koinViewModel private fun PreviewDeviceListScreenContent( @PreviewParameter(DeviceListUiStatePreviewParameterProvider::class) state: DeviceListUiState ) { - AppTheme { DeviceListScreen(state = state) } + AppTheme { DeviceListScreen(state = state, SnackbarHostState(), {}, {}, {}, {}, {}) } } data class DeviceListNavArgs(val accountNumber: AccountNumber) @@ -139,11 +139,11 @@ fun DeviceList( fun DeviceListScreen( state: DeviceListUiState, snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, - onBackClick: () -> Unit = {}, - onContinueWithLogin: () -> Unit = {}, - onSettingsClicked: () -> Unit = {}, - onTryAgainClicked: () -> Unit = {}, - navigateToRemoveDeviceConfirmationDialog: (device: Device) -> Unit = {}, + onBackClick: () -> Unit, + onContinueWithLogin: () -> Unit, + onSettingsClicked: () -> Unit, + onTryAgainClicked: () -> Unit, + navigateToRemoveDeviceConfirmationDialog: (device: Device) -> Unit, ) { ScaffoldWithTopBar( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt index 39f8f2fd8401..d0a334d561bc 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt @@ -46,7 +46,7 @@ private fun PreviewDeviceRevokedScreen( @PreviewParameter(DeviceRevokedUiStatePreviewParameterProvider::class) state: DeviceRevokedUiState ) { - AppTheme { DeviceRevokedScreen(state = state) } + AppTheme { DeviceRevokedScreen(state = state, {}, {}) } } @Destination @@ -76,8 +76,8 @@ fun DeviceRevoked(navigator: DestinationsNavigator) { @Composable fun DeviceRevokedScreen( state: DeviceRevokedUiState, - onSettingsClicked: () -> Unit = {}, - onGoToLoginClicked: () -> Unit = {}, + onSettingsClicked: () -> Unit, + onGoToLoginClicked: () -> Unit, ) { val topColor = if (state == DeviceRevokedUiState.SECURED) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreen.kt index 6596f7c3431e..768c43d588c1 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditApiAccessMethodScreen.kt @@ -79,7 +79,23 @@ private fun PreviewEditApiAccessMethodScreen( @PreviewParameter(EditApiAccessMethodUiStatePreviewParameterProvider::class) state: EditApiAccessMethodUiState ) { - AppTheme { EditApiAccessMethodScreen(state = state) } + AppTheme { + EditApiAccessMethodScreen( + state = state, + SnackbarHostState(), + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + ) + } } data class EditApiAccessMethodNavArgs(val accessMethodId: ApiAccessMethodId?) @@ -192,17 +208,17 @@ fun EditApiAccessMethod( fun EditApiAccessMethodScreen( state: EditApiAccessMethodUiState, snackbarHostState: SnackbarHostState = SnackbarHostState(), - onNameChanged: (String) -> Unit = {}, - onTypeSelected: (ApiAccessMethodTypes) -> Unit = {}, - onIpChanged: (String) -> Unit = {}, - onPortChanged: (String) -> Unit = {}, - onPasswordChanged: (String) -> Unit = {}, - onCipherChange: (Cipher) -> Unit = {}, - onToggleAuthenticationEnabled: (Boolean) -> Unit = {}, - onUsernameChanged: (String) -> Unit = {}, - onTestMethod: () -> Unit = {}, - onAddMethod: () -> Unit = {}, - onNavigateBack: () -> Unit = {}, + onNameChanged: (String) -> Unit, + onTypeSelected: (ApiAccessMethodTypes) -> Unit, + onIpChanged: (String) -> Unit, + onPortChanged: (String) -> Unit, + onPasswordChanged: (String) -> Unit, + onCipherChange: (Cipher) -> Unit, + onToggleAuthenticationEnabled: (Boolean) -> Unit, + onUsernameChanged: (String) -> Unit, + onTestMethod: () -> Unit, + onAddMethod: () -> Unit, + onNavigateBack: () -> Unit, ) { ScaffoldWithSmallTopBar( snackbarHostState = snackbarHostState, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt index 30f5491c732f..513bc636d413 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/EditCustomListScreen.kt @@ -62,7 +62,7 @@ private fun PreviewEditCustomListScreen( @PreviewParameter(EditCustomListUiStatePreviewParameterProvider::class) state: EditCustomListUiState ) { - AppTheme { EditCustomListScreen(state = state) } + AppTheme { EditCustomListScreen(state = state, { _, _ -> }, { _, _ -> }, {}, {}) } } data class EditCustomListNavArgs(val customListId: CustomListId) @@ -116,10 +116,10 @@ fun EditCustomList( @Composable fun EditCustomListScreen( state: EditCustomListUiState, - onDeleteList: (id: CustomListId, name: CustomListName) -> Unit = { _, _ -> }, - onNameClicked: (id: CustomListId, name: CustomListName) -> Unit = { _, _ -> }, - onLocationsClicked: (CustomListId) -> Unit = {}, - onBackClick: () -> Unit = {}, + onDeleteList: (id: CustomListId, name: CustomListName) -> Unit, + onNameClicked: (id: CustomListId, name: CustomListName) -> Unit, + onLocationsClicked: (CustomListId) -> Unit, + onBackClick: () -> Unit, ) { ScaffoldWithMediumTopBar( appBarTitle = stringResource(id = R.string.edit_list), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreen.kt index 9b3142b161a4..60788bffd593 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/FilterScreen.kt @@ -64,6 +64,8 @@ private fun PreviewFilterScreen( onSelectedOwnership = {}, onSelectedProvider = { _, _ -> }, onAllProviderCheckChange = {}, + onBackClick = {}, + onApplyClick = {}, ) } } @@ -92,11 +94,11 @@ fun Filter(navigator: DestinationsNavigator) { @Composable fun FilterScreen( state: RelayFilterUiState, - onBackClick: () -> Unit = {}, - onApplyClick: () -> Unit = {}, - onSelectedOwnership: (ownership: Ownership?) -> Unit = {}, - onAllProviderCheckChange: (isChecked: Boolean) -> Unit = {}, - onSelectedProvider: (checked: Boolean, provider: Provider) -> Unit = { _, _ -> }, + onBackClick: () -> Unit, + onApplyClick: () -> Unit, + onSelectedOwnership: (ownership: Ownership?) -> Unit, + onAllProviderCheckChange: (isChecked: Boolean) -> Unit, + onSelectedProvider: (checked: Boolean, provider: Provider) -> Unit, ) { var providerExpanded by rememberSaveable { mutableStateOf(false) } var ownershipExpanded by rememberSaveable { mutableStateOf(false) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ImportOverridesByTextScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ImportOverridesByTextScreen.kt index 3abcc5842d22..228cfff16f42 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ImportOverridesByTextScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ImportOverridesByTextScreen.kt @@ -29,11 +29,12 @@ import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.compose.component.MullvadSmallTopBar import net.mullvad.mullvadvpn.compose.textfield.mullvadWhiteTextFieldColors import net.mullvad.mullvadvpn.compose.transitions.DefaultTransition +import net.mullvad.mullvadvpn.lib.theme.AppTheme @Preview @Composable private fun PreviewImportOverridesByText() { - ImportOverridesByTextScreen({}, {}) + AppTheme { ImportOverridesByTextScreen({}, {}) } } @Destination(style = DefaultTransition::class) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt index ba799d55c1a5..fef29516c53c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt @@ -95,7 +95,7 @@ import org.koin.androidx.compose.koinViewModel private fun PreviewLoginScreen( @PreviewParameter(LoginUiStatePreviewParameterProvider::class) state: LoginUiState ) { - AppTheme { LoginScreen(state = state) } + AppTheme { LoginScreen(state = state, SnackbarHostState(), {}, {}, {}, {}, {}) } } private const val TOP_SPACER_WEIGHT = 1f @@ -172,11 +172,11 @@ fun Login( private fun LoginScreen( state: LoginUiState, snackbarHostState: SnackbarHostState = SnackbarHostState(), - onLoginClick: (String) -> Unit = {}, - onCreateAccountClick: () -> Unit = {}, - onDeleteHistoryClick: () -> Unit = {}, - onAccountNumberChange: (String) -> Unit = {}, - onSettingsClick: () -> Unit = {}, + onLoginClick: (String) -> Unit, + onCreateAccountClick: () -> Unit, + onDeleteHistoryClick: () -> Unit, + onAccountNumberChange: (String) -> Unit, + onSettingsClick: () -> Unit, ) { ScaffoldWithTopBar( snackbarHostState = snackbarHostState, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MultihopScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MultihopScreen.kt index 5491fc624c06..0c7be2aadce3 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MultihopScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MultihopScreen.kt @@ -33,7 +33,7 @@ import org.koin.androidx.compose.koinViewModel @Preview @Composable private fun PreviewMultihopScreen() { - AppTheme { MultihopScreen(state = MultihopUiState(false)) } + AppTheme { MultihopScreen(state = MultihopUiState(false), {}, {}) } } @Destination(style = SlideInFromRightTransition::class) @@ -51,8 +51,8 @@ fun Multihop(navigator: DestinationsNavigator) { @Composable fun MultihopScreen( state: MultihopUiState, - onMultihopClick: (enable: Boolean) -> Unit = {}, - onBackClick: () -> Unit = {}, + onMultihopClick: (enable: Boolean) -> Unit, + onBackClick: () -> Unit, ) { ScaffoldWithMediumTopBar( appBarTitle = stringResource(id = R.string.multihop), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt index 10fc9cd3afb6..95056dba4a13 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt @@ -69,7 +69,7 @@ import org.koin.androidx.compose.koinViewModel private fun PreviewOutOfTimeScreen( @PreviewParameter(OutOfTimeScreenPreviewParameterProvider::class) state: OutOfTimeUiState ) { - AppTheme { OutOfTimeScreen(state = state) } + AppTheme { OutOfTimeScreen(state = state, SnackbarHostState(), {}, {}, {}, {}, {}, {}, {}) } } @Destination(style = HomeTransition::class) @@ -137,14 +137,14 @@ fun OutOfTime( @Composable fun OutOfTimeScreen( state: OutOfTimeUiState, - snackbarHostState: SnackbarHostState = SnackbarHostState(), - onDisconnectClick: () -> Unit = {}, - onSitePaymentClick: () -> Unit = {}, - onRedeemVoucherClick: () -> Unit = {}, - onSettingsClick: () -> Unit = {}, - onAccountClick: () -> Unit = {}, - onPurchaseBillingProductClick: (ProductId) -> Unit = { _ -> }, - navigateToVerificationPendingDialog: () -> Unit = {}, + snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, + onDisconnectClick: () -> Unit, + onSitePaymentClick: () -> Unit, + onRedeemVoucherClick: () -> Unit, + onSettingsClick: () -> Unit, + onAccountClick: () -> Unit, + onPurchaseBillingProductClick: (ProductId) -> Unit, + navigateToVerificationPendingDialog: () -> Unit, ) { val scrollState = rememberScrollState() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt index 2e5688da81f5..a3338d2e24c9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt @@ -72,7 +72,7 @@ private fun PreviewReportProblemScreen( @PreviewParameter(ReportProblemUiStatePreviewParameterProvider::class) state: ReportProblemUiState ) { - AppTheme { ReportProblemScreen(state = state) } + AppTheme { ReportProblemScreen(state = state, {}, {}, {}, {}, {}, {}) } } @Destination(style = SlideInFromRightTransition::class) @@ -115,12 +115,12 @@ fun ReportProblem( @Composable private fun ReportProblemScreen( state: ReportProblemUiState, - onSendReport: () -> Unit = {}, - onClearSendResult: () -> Unit = {}, - onNavigateToViewLogs: () -> Unit = {}, - onEmailChanged: (String) -> Unit = {}, - onDescriptionChanged: (String) -> Unit = {}, - onBackClick: () -> Unit = {}, + onSendReport: () -> Unit, + onClearSendResult: () -> Unit, + onNavigateToViewLogs: () -> Unit, + onEmailChanged: (String) -> Unit, + onDescriptionChanged: (String) -> Unit, + onBackClick: () -> Unit, ) { ScaffoldWithMediumTopBar( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt index 15754990f22b..6bef82fd9ec5 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt @@ -57,7 +57,7 @@ import org.koin.androidx.compose.koinViewModel private fun PreviewSettingsScreen( @PreviewParameter(SettingsUiStatePreviewParameterProvider::class) state: SettingsUiState ) { - AppTheme { SettingsScreen(state = state) } + AppTheme { SettingsScreen(state = state, {}, {}, {}, {}, {}, {}, {}, {}) } } @OptIn(ExperimentalMaterial3Api::class) @@ -81,18 +81,17 @@ fun Settings(navigator: DestinationsNavigator) { ) } -@ExperimentalMaterial3Api @Composable fun SettingsScreen( state: SettingsUiState, - onVpnSettingCellClick: () -> Unit = {}, - onSplitTunnelingCellClick: () -> Unit = {}, - onAppInfoClick: () -> Unit = {}, - onReportProblemCellClick: () -> Unit = {}, - onApiAccessClick: () -> Unit = {}, - onMultihopClick: () -> Unit = {}, - onDaitaClick: () -> Unit = {}, - onBackClick: () -> Unit = {}, + onVpnSettingCellClick: () -> Unit, + onSplitTunnelingCellClick: () -> Unit, + onAppInfoClick: () -> Unit, + onReportProblemCellClick: () -> Unit, + onApiAccessClick: () -> Unit, + onMultihopClick: () -> Unit, + onDaitaClick: () -> Unit, + onBackClick: () -> Unit, ) { ScaffoldWithMediumTopBar( appBarTitle = stringResource(id = R.string.settings), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ShadowsocksSettingsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ShadowsocksSettingsScreen.kt index 0ea4b7fbb024..05a99a1c4915 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ShadowsocksSettingsScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ShadowsocksSettingsScreen.kt @@ -39,7 +39,10 @@ import org.koin.androidx.compose.koinViewModel private fun PreviewShadowsocksSettingsScreen() { AppTheme { ShadowsocksSettingsScreen( - state = ShadowsocksSettingsState(port = Constraint.Any, validPortRanges = emptyList()) + state = ShadowsocksSettingsState(port = Constraint.Any, validPortRanges = emptyList()), + {}, + {}, + {}, ) } } @@ -82,9 +85,9 @@ fun ShadowsocksSettings( @Composable fun ShadowsocksSettingsScreen( state: ShadowsocksSettingsState, - navigateToCustomPortDialog: () -> Unit = {}, - onObfuscationPortSelected: (Constraint) -> Unit = {}, - onBackClick: () -> Unit = {}, + navigateToCustomPortDialog: () -> Unit, + onObfuscationPortSelected: (Constraint) -> Unit, + onBackClick: () -> Unit, ) { ScaffoldWithMediumTopBar( appBarTitle = stringResource(id = R.string.shadowsocks), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt index 0dddd926fcd8..5061655003c4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt @@ -59,7 +59,7 @@ private fun PreviewSplitTunnelingScreen( @PreviewParameter(SplitTunnelingUiStatePreviewParameterProvider::class) state: SplitTunnelingUiState ) { - AppTheme { SplitTunnelingScreen(state = state) } + AppTheme { SplitTunnelingScreen(state = state, {}, {}, {}, {}, {}, { null }) } } @Destination(style = SlideInFromRightTransition::class) @@ -86,12 +86,12 @@ fun SplitTunneling(navigator: DestinationsNavigator) { @Composable fun SplitTunnelingScreen( state: SplitTunnelingUiState, - onEnableSplitTunneling: (Boolean) -> Unit = {}, - onShowSystemAppsClick: (show: Boolean) -> Unit = {}, - onExcludeAppClick: (packageName: String) -> Unit = {}, - onIncludeAppClick: (packageName: String) -> Unit = {}, - onBackClick: () -> Unit = {}, - onResolveIcon: (String) -> Bitmap? = { null }, + onEnableSplitTunneling: (Boolean) -> Unit, + onShowSystemAppsClick: (show: Boolean) -> Unit, + onExcludeAppClick: (packageName: String) -> Unit, + onIncludeAppClick: (packageName: String) -> Unit, + onBackClick: () -> Unit, + onResolveIcon: (String) -> Bitmap?, ) { val focusManager = LocalFocusManager.current diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/Udp2TcpSettingsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/Udp2TcpSettingsScreen.kt index 436d8c902093..079311f123c5 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/Udp2TcpSettingsScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/Udp2TcpSettingsScreen.kt @@ -31,7 +31,9 @@ import org.koin.androidx.compose.koinViewModel @Preview @Composable private fun PreviewUdp2TcpSettingsScreen() { - AppTheme { Udp2TcpSettingsScreen(state = Udp2TcpSettingsState(port = Constraint.Any)) } + AppTheme { + Udp2TcpSettingsScreen(state = Udp2TcpSettingsState(port = Constraint.Any), {}, {}, {}) + } } @Destination(style = SlideInFromRightTransition::class) @@ -51,9 +53,9 @@ fun Udp2TcpSettings(navigator: DestinationsNavigator) { @Composable fun Udp2TcpSettingsScreen( state: Udp2TcpSettingsState, - onObfuscationPortSelected: (Constraint) -> Unit = {}, - navigateUdp2TcpInfo: () -> Unit = {}, - onBackClick: () -> Unit = {}, + onObfuscationPortSelected: (Constraint) -> Unit, + navigateUdp2TcpInfo: () -> Unit, + onBackClick: () -> Unit, ) { ScaffoldWithMediumTopBar( appBarTitle = stringResource(id = R.string.upd_over_tcp), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ViewLogsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ViewLogsScreen.kt index 7b227f9a9f44..621fb42885eb 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ViewLogsScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ViewLogsScreen.kt @@ -63,7 +63,7 @@ import org.koin.androidx.compose.koinViewModel private fun PreviewViewLogsScreen( @PreviewParameter(ViewLogsUiStatePreviewParameterProvider::class) state: ViewLogsUiState ) { - AppTheme { ViewLogsScreen(state = state) } + AppTheme { ViewLogsScreen(state = state, {}) } } @Destination(style = SlideInFromRightTransition::class) @@ -75,7 +75,7 @@ fun ViewLogs(navigator: DestinationsNavigator) { } @Composable -fun ViewLogsScreen(state: ViewLogsUiState, onBackClick: () -> Unit = {}) { +fun ViewLogsScreen(state: ViewLogsUiState, onBackClick: () -> Unit) { val snackbarHostState = remember { SnackbarHostState() } val clipboardHandle = createCopyToClipboardHandle(snackbarHostState = snackbarHostState) Scaffold( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt index 6489b53e79d7..c44972e781ae 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt @@ -129,6 +129,19 @@ private fun PreviewVpnSettings( onSelectObfuscationMode = {}, onSelectQuantumResistanceSetting = {}, onWireguardPortSelected = {}, + navigateToShadowSocksSettings = {}, + navigateToUdp2TcpSettings = {}, + onToggleAutoStartAndConnectOnBoot = { _ -> }, + navigateToMalwareInfo = {}, + navigateToContentBlockersInfo = {}, + navigateToAutoConnectScreen = {}, + navigateToCustomDnsInfo = {}, + navigateToObfuscationInfo = {}, + navigateToQuantumResistanceInfo = {}, + navigateToWireguardPortInfo = {}, + navigateToLocalNetworkSharingInfo = {}, + navigateToWireguardPortDialog = {}, + navigateToServerIpOverrides = {}, ) } } @@ -255,39 +268,39 @@ fun VpnSettings( ) } -@Suppress("LongMethod") +@Suppress("LongMethod", "LongParameterList") @OptIn(ExperimentalFoundationApi::class) @Composable fun VpnSettingsScreen( state: VpnSettingsUiState, snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, - navigateToContentBlockersInfo: () -> Unit = {}, - navigateToAutoConnectScreen: () -> Unit = {}, - navigateToCustomDnsInfo: () -> Unit = {}, - navigateToMalwareInfo: () -> Unit = {}, - navigateToObfuscationInfo: () -> Unit = {}, - navigateToQuantumResistanceInfo: () -> Unit = {}, - navigateToWireguardPortInfo: (availablePortRanges: List) -> Unit = {}, - navigateToLocalNetworkSharingInfo: () -> Unit = {}, - navigateToWireguardPortDialog: () -> Unit = {}, - navigateToServerIpOverrides: () -> Unit = {}, - onToggleBlockTrackers: (Boolean) -> Unit = {}, - onToggleBlockAds: (Boolean) -> Unit = {}, - onToggleBlockMalware: (Boolean) -> Unit = {}, - onToggleLocalNetworkSharing: (Boolean) -> Unit = {}, - onToggleBlockAdultContent: (Boolean) -> Unit = {}, - onToggleBlockGambling: (Boolean) -> Unit = {}, - onToggleBlockSocialMedia: (Boolean) -> Unit = {}, - navigateToMtuDialog: (mtu: Mtu?) -> Unit = {}, - navigateToDns: (index: Int?, address: String?) -> Unit = { _, _ -> }, - onToggleDnsClick: (Boolean) -> Unit = {}, - onBackClick: () -> Unit = {}, - onSelectObfuscationMode: (obfuscationMode: ObfuscationMode) -> Unit = {}, - onSelectQuantumResistanceSetting: (quantumResistant: QuantumResistantState) -> Unit = {}, - onWireguardPortSelected: (port: Constraint) -> Unit = {}, - navigateToShadowSocksSettings: () -> Unit = {}, - navigateToUdp2TcpSettings: () -> Unit = {}, - onToggleAutoStartAndConnectOnBoot: (Boolean) -> Unit = {}, + navigateToContentBlockersInfo: () -> Unit, + navigateToAutoConnectScreen: () -> Unit, + navigateToCustomDnsInfo: () -> Unit, + navigateToMalwareInfo: () -> Unit, + navigateToObfuscationInfo: () -> Unit, + navigateToQuantumResistanceInfo: () -> Unit, + navigateToWireguardPortInfo: (availablePortRanges: List) -> Unit, + navigateToLocalNetworkSharingInfo: () -> Unit, + navigateToWireguardPortDialog: () -> Unit, + navigateToServerIpOverrides: () -> Unit, + onToggleBlockTrackers: (Boolean) -> Unit, + onToggleBlockAds: (Boolean) -> Unit, + onToggleBlockMalware: (Boolean) -> Unit, + onToggleLocalNetworkSharing: (Boolean) -> Unit, + onToggleBlockAdultContent: (Boolean) -> Unit, + onToggleBlockGambling: (Boolean) -> Unit, + onToggleBlockSocialMedia: (Boolean) -> Unit, + navigateToMtuDialog: (mtu: Mtu?) -> Unit, + navigateToDns: (index: Int?, address: String?) -> Unit, + onToggleDnsClick: (Boolean) -> Unit, + onBackClick: () -> Unit, + onSelectObfuscationMode: (obfuscationMode: ObfuscationMode) -> Unit, + onSelectQuantumResistanceSetting: (quantumResistant: QuantumResistantState) -> Unit, + onWireguardPortSelected: (port: Constraint) -> Unit, + navigateToShadowSocksSettings: () -> Unit, + navigateToUdp2TcpSettings: () -> Unit, + onToggleAutoStartAndConnectOnBoot: (Boolean) -> Unit, ) { var expandContentBlockersState by rememberSaveable { mutableStateOf(false) } val biggerPadding = 54.dp diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreen.kt index fc810e68827b..737baa6f0c39 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreen.kt @@ -80,7 +80,25 @@ private fun PreviewSearchLocationScreen( @PreviewParameter(SearchLocationsUiStatePreviewParameterProvider::class) state: SearchLocationUiState ) { - AppTheme { SearchLocationScreen(state = state) } + AppTheme { + SearchLocationScreen( + state = state, + SnackbarHostState(), + {}, + { _, _, _ -> }, + {}, + {}, + {}, + { _, _ -> }, + { _, _ -> }, + {}, + {}, + {}, + {}, + {}, + {}, + ) + } } data class SearchLocationNavArgs(val relayListType: RelayListType) @@ -198,24 +216,20 @@ fun SearchLocation( @Composable fun SearchLocationScreen( state: SearchLocationUiState, - snackbarHostState: SnackbarHostState = SnackbarHostState(), - onSelectRelay: (RelayItem) -> Unit = {}, - onToggleExpand: (RelayItemId, CustomListId?, Boolean) -> Unit = { _, _, _ -> }, - onSearchInputChanged: (String) -> Unit = {}, - onCreateCustomList: (location: RelayItem.Location?) -> Unit = {}, - onEditCustomLists: () -> Unit = {}, - onAddLocationToList: (location: RelayItem.Location, customList: RelayItem.CustomList) -> Unit = - { _, _ -> - }, - onRemoveLocationFromList: (location: RelayItem.Location, customListId: CustomListId) -> Unit = - { _, _ -> - }, - onEditCustomListName: (RelayItem.CustomList) -> Unit = {}, - onEditLocationsCustomList: (RelayItem.CustomList) -> Unit = {}, - onDeleteCustomList: (RelayItem.CustomList) -> Unit = {}, - onRemoveOwnershipFilter: () -> Unit = {}, - onRemoveProviderFilter: () -> Unit = {}, - onGoBack: () -> Unit = {}, + snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, + onSelectRelay: (RelayItem) -> Unit, + onToggleExpand: (RelayItemId, CustomListId?, Boolean) -> Unit, + onSearchInputChanged: (String) -> Unit, + onCreateCustomList: (location: RelayItem.Location?) -> Unit, + onEditCustomLists: () -> Unit, + onAddLocationToList: (location: RelayItem.Location, customList: RelayItem.CustomList) -> Unit, + onRemoveLocationFromList: (location: RelayItem.Location, customListId: CustomListId) -> Unit, + onEditCustomListName: (RelayItem.CustomList) -> Unit, + onEditLocationsCustomList: (RelayItem.CustomList) -> Unit, + onDeleteCustomList: (RelayItem.CustomList) -> Unit, + onRemoveOwnershipFilter: () -> Unit, + onRemoveProviderFilter: () -> Unit, + onGoBack: () -> Unit, ) { val backgroundColor = MaterialTheme.colorScheme.surface val onBackgroundColor = MaterialTheme.colorScheme.onSurface diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreen.kt index 079c1e175b61..cac37053ad6c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreen.kt @@ -83,7 +83,27 @@ private fun PreviewSelectLocationScreen( @PreviewParameter(SelectLocationsUiStatePreviewParameterProvider::class) state: SelectLocationUiState ) { - AppTheme { SelectLocationScreen(state = state) } + AppTheme { + SelectLocationScreen( + state = state, + SnackbarHostState(), + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + { _, _ -> }, + { _, _ -> }, + {}, + {}, + {}, + {}, + {}, + ) + } } @SuppressLint("CheckResult") @@ -202,30 +222,26 @@ fun SelectLocation( ) } -@Suppress("LongMethod") +@Suppress("LongMethod", "LongParameterList") @Composable fun SelectLocationScreen( state: SelectLocationUiState, snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, - onSelectRelay: (item: RelayItem) -> Unit = {}, - onSearchClick: (RelayListType) -> Unit = {}, - onBackClick: () -> Unit = {}, - onFilterClick: () -> Unit = {}, - onCreateCustomList: (location: RelayItem.Location?) -> Unit = {}, - onEditCustomLists: () -> Unit = {}, - removeOwnershipFilter: () -> Unit = {}, - removeProviderFilter: () -> Unit = {}, - onAddLocationToList: (location: RelayItem.Location, customList: RelayItem.CustomList) -> Unit = - { _, _ -> - }, - onRemoveLocationFromList: (location: RelayItem.Location, customListId: CustomListId) -> Unit = - { _, _ -> - }, - onEditCustomListName: (RelayItem.CustomList) -> Unit = {}, - onEditLocationsCustomList: (RelayItem.CustomList) -> Unit = {}, - onDeleteCustomList: (RelayItem.CustomList) -> Unit = {}, - onSelectRelayList: (RelayListType) -> Unit = {}, - openDaitaSettings: () -> Unit = {}, + onSelectRelay: (item: RelayItem) -> Unit, + onSearchClick: (RelayListType) -> Unit, + onBackClick: () -> Unit, + onFilterClick: () -> Unit, + onCreateCustomList: (location: RelayItem.Location?) -> Unit, + onEditCustomLists: () -> Unit, + removeOwnershipFilter: () -> Unit, + removeProviderFilter: () -> Unit, + onAddLocationToList: (location: RelayItem.Location, customList: RelayItem.CustomList) -> Unit, + onRemoveLocationFromList: (location: RelayItem.Location, customListId: CustomListId) -> Unit, + onEditCustomListName: (RelayItem.CustomList) -> Unit, + onEditLocationsCustomList: (RelayItem.CustomList) -> Unit, + onDeleteCustomList: (RelayItem.CustomList) -> Unit, + onSelectRelayList: (RelayListType) -> Unit, + openDaitaSettings: () -> Unit, ) { val backgroundColor = MaterialTheme.colorScheme.surface diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/Snackbar.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/Snackbar.kt index 88ec729d4639..f67c1e732007 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/Snackbar.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/Snackbar.kt @@ -8,15 +8,15 @@ import androidx.compose.material3.SnackbarResult suspend fun SnackbarHostState.showSnackbarImmediately( message: String, actionLabel: String? = null, - onAction: (() -> Unit) = {}, + onAction: (() -> Unit)? = null, withDismissAction: Boolean = false, - onDismiss: (() -> Unit) = {}, + onDismiss: (() -> Unit)? = null, duration: SnackbarDuration = if (actionLabel == null) SnackbarDuration.Short else SnackbarDuration.Indefinite, ) { currentSnackbarData?.dismiss() when (showSnackbar(message, actionLabel, withDismissAction, duration)) { - SnackbarResult.ActionPerformed -> onAction() - SnackbarResult.Dismissed -> onDismiss() + SnackbarResult.ActionPerformed -> onAction?.invoke() + SnackbarResult.Dismissed -> onDismiss?.invoke() } }