Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…fork into filter-providers
  • Loading branch information
MaryamShaghaghi committed Nov 28, 2023
2 parents 7adf7f7 + f976843 commit 27fe90e
Show file tree
Hide file tree
Showing 22 changed files with 445 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package net.mullvad.mullvadvpn.compose.screen

import androidx.compose.ui.test.junit4.createComposeRule
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 io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.verify
import net.mullvad.mullvadvpn.compose.setContentWithTheme
import net.mullvad.mullvadvpn.compose.state.VoucherDialogState
import net.mullvad.mullvadvpn.compose.state.VoucherDialogUiState
import net.mullvad.mullvadvpn.compose.test.VOUCHER_INPUT_TEST_TAG
import net.mullvad.mullvadvpn.util.VoucherRegexHelper
import org.junit.Before
import org.junit.Rule
import org.junit.Test

class RedeemVoucherDialogTest {
@get:Rule val composeTestRule = createComposeRule()

@Before
fun setup() {
mockkObject(VoucherRegexHelper)
}

@Test
fun testDismissDialog() {
// Arrange
val mockedClickHandler: (Boolean) -> Unit = mockk(relaxed = true)
composeTestRule.setContentWithTheme {
RedeemVoucherDialogScreen(
uiState = VoucherDialogUiState.INITIAL,
onVoucherInputChange = {},
onRedeem = {},
onDismiss = mockedClickHandler
)
}

// Act
composeTestRule.onNodeWithText(CANCEL_BUTTON_TEXT).performClick()

// Assert
verify { mockedClickHandler.invoke(false) }
}

@Test
fun testDismissDialogAfterSuccessfulRedeem() {
// Arrange
val mockedClickHandler: (Boolean) -> Unit = mockk(relaxed = true)
composeTestRule.setContentWithTheme {
RedeemVoucherDialogScreen(
uiState =
VoucherDialogUiState(voucherViewModelState = VoucherDialogState.Success(0)),
onVoucherInputChange = {},
onRedeem = {},
onDismiss = mockedClickHandler
)
}

// Act
composeTestRule.onNodeWithText(GOT_IT_BUTTON_TEXT).performClick()

// Assert
verify { mockedClickHandler.invoke(true) }
}

@Test
fun testInsertVoucher() {
// Arrange
val mockedClickHandler: (String) -> Unit = mockk(relaxed = true)
composeTestRule.setContentWithTheme {
RedeemVoucherDialogScreen(
uiState = VoucherDialogUiState(),
onVoucherInputChange = mockedClickHandler,
onRedeem = {},
onDismiss = {}
)
}

// Act
composeTestRule.onNodeWithTag(VOUCHER_INPUT_TEST_TAG).performTextInput(DUMMY_VOUCHER)

// Assert
verify { mockedClickHandler.invoke(DUMMY_VOUCHER) }
}

@Test
fun testVerifyingState() {
// Arrange
composeTestRule.setContentWithTheme {
RedeemVoucherDialogScreen(
uiState =
VoucherDialogUiState(voucherViewModelState = VoucherDialogState.Verifying),
onVoucherInputChange = {},
onRedeem = {},
onDismiss = {}
)
}

// Assert
composeTestRule.onNodeWithText("Verifying voucher…").assertExists()
}

@Test
fun testSuccessState() {
// Arrange
composeTestRule.setContentWithTheme {
RedeemVoucherDialogScreen(
uiState =
VoucherDialogUiState(voucherViewModelState = VoucherDialogState.Success(0)),
onVoucherInputChange = {},
onRedeem = {},
onDismiss = {}
)
}

// Assert
composeTestRule.onNodeWithText("Voucher was successfully redeemed.").assertExists()
}

@Test
fun testErrorState() {
// Arrange
composeTestRule.setContentWithTheme {
RedeemVoucherDialogScreen(
uiState =
VoucherDialogUiState(
voucherViewModelState = VoucherDialogState.Error(ERROR_MESSAGE)
),
onVoucherInputChange = {},
onRedeem = {},
onDismiss = {}
)
}

// Assert
composeTestRule.onNodeWithText(ERROR_MESSAGE).assertExists()
}

companion object {
private const val REDEEM_BUTTON_TEXT = "Redeem"
private const val CANCEL_BUTTON_TEXT = "Cancel"
private const val GOT_IT_BUTTON_TEXT = "Got it!"
private const val DUMMY_VOUCHER = "DUMMY____VOUCHER"
private const val ERROR_MESSAGE = "error_message"
}
}
7 changes: 7 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,12 @@
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
<provider android:name="net.mullvad.mullvadvpn.provider.MullvadFileProvider"
android:authorities="${applicationId}.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,15 @@ fun MullvadTopBarWithDeviceName(
text =
stringResource(
id = R.string.top_bar_time_left,
pluralStringResource(
id = R.plurals.days,
daysLeftUntilExpiry,
daysLeftUntilExpiry
)
if (daysLeftUntilExpiry >= 0) {
pluralStringResource(
id = R.plurals.days,
daysLeftUntilExpiry,
daysLeftUntilExpiry
)
} else {
stringResource(id = R.string.out_of_time)
}
),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onBackground.copy(alpha = AlphaTopBar)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
Expand All @@ -29,7 +30,8 @@ import net.mullvad.mullvadvpn.compose.button.VariantButton
import net.mullvad.mullvadvpn.compose.component.MullvadCircularProgressIndicatorSmall
import net.mullvad.mullvadvpn.compose.state.VoucherDialogState
import net.mullvad.mullvadvpn.compose.state.VoucherDialogUiState
import net.mullvad.mullvadvpn.compose.textfield.GroupedTextField
import net.mullvad.mullvadvpn.compose.test.VOUCHER_INPUT_TEST_TAG
import net.mullvad.mullvadvpn.compose.textfield.CustomTextField
import net.mullvad.mullvadvpn.compose.util.MAX_VOUCHER_LENGTH
import net.mullvad.mullvadvpn.compose.util.vouchersVisualTransformation
import net.mullvad.mullvadvpn.constant.VOUCHER_LENGTH
Expand Down Expand Up @@ -217,23 +219,21 @@ private fun EnterVoucherBody(
onVoucherInputChange: (String) -> Unit = {},
onRedeem: (voucherCode: String) -> Unit
) {
GroupedTextField(
CustomTextField(
value = uiState.voucherInput,
onSubmit = { input ->
if (uiState.voucherInput.length == VOUCHER_LENGTH) {
onRedeem(input)
}
},
onValueChanged = { input -> onVoucherInputChange(input.uppercase()) },
onValueChanged = { input -> onVoucherInputChange(input) },
isValidValue =
uiState.voucherInput.isEmpty() || uiState.voucherInput.length == MAX_VOUCHER_LENGTH,
keyboardType = KeyboardType.Password,
placeholderText = stringResource(id = R.string.voucher_hint),
visualTransformation = vouchersVisualTransformation(),
maxCharLength = VOUCHER_LENGTH,
isDigitsOnlyAllowed = false,
isEnabled = true,
validateRegex = "^[A-Za-z0-9]*$".toRegex()
modifier = Modifier.testTag(VOUCHER_INPUT_TEST_TAG)
)
Spacer(modifier = Modifier.height(Dimens.smallPadding))
Row(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package net.mullvad.mullvadvpn.compose.screen

import android.content.Context
import android.content.Intent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
Expand All @@ -15,21 +14,30 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.component.MullvadCircularProgressIndicatorMedium
import net.mullvad.mullvadvpn.compose.component.MullvadMediumTopBar
import net.mullvad.mullvadvpn.compose.component.MullvadSnackbar
import net.mullvad.mullvadvpn.compose.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar
import net.mullvad.mullvadvpn.compose.util.createCopyToClipboardHandle
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.lib.theme.color.AlphaScrollbar
import net.mullvad.mullvadvpn.provider.getLogsShareIntent
import net.mullvad.mullvadvpn.viewmodel.ViewLogsUiState

@Preview
Expand All @@ -52,13 +60,31 @@ fun ViewLogsScreen(
) {
val context = LocalContext.current

val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
val clipboardHandle = createCopyToClipboardHandle(snackbarHostState = snackbarHostState)
Scaffold(
snackbarHost = {
SnackbarHost(
snackbarHostState,
snackbar = { snackbarData -> MullvadSnackbar(snackbarData = snackbarData) }
)
},
topBar = {
MullvadMediumTopBar(
title = stringResource(id = R.string.view_logs),
navigationIcon = { NavigateBackIconButton(onBackClick) },
actions = {
IconButton(onClick = { shareText(context, uiState.text()) }) {
val clipboardToastMessage = stringResource(R.string.copied_logs_to_clipboard)
IconButton(
onClick = { clipboardHandle(uiState.text(), clipboardToastMessage) }
) {
Icon(
painter = painterResource(id = R.drawable.icon_copy),
contentDescription = null
)
}
IconButton(onClick = { scope.launch { shareText(context, uiState.text()) } }) {
Icon(imageVector = Icons.Default.Share, contentDescription = null)
}
}
Expand Down Expand Up @@ -101,14 +127,7 @@ fun ViewLogsScreen(
}
}

private fun shareText(context: Context, text: String) {
val sendIntent: Intent =
Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, text)
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)

private fun shareText(context: Context, logContent: String) {
val shareIntent = context.getLogsShareIntent("Share logs", logContent)
context.startActivity(shareIntent)
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ const val PLAY_PAYMENT_INFO_ICON_TEST_TAG = "play_payment_info_icon_test_tag"

const val LOGIN_TITLE_TEST_TAG = "login_title_test_tag"
const val LOGIN_INPUT_TEST_TAG = "login_input_test_tag"

// VoucherDialog
const val VOUCHER_INPUT_TEST_TAG = "voucher_input_test_tag"

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ val uiModule = module {
viewModel {
ChangelogViewModel(get(), BuildConfig.VERSION_CODE, BuildConfig.ALWAYS_SHOW_CHANGELOG)
}
viewModel { ConnectViewModel(get(), get(), get(), get(), get(), get()) }
viewModel { ConnectViewModel(get(), get(), get(), get(), get(), get(), get()) }
viewModel { DeviceListViewModel(get(), get()) }
viewModel { DeviceRevokedViewModel(get(), get()) }
viewModel { LoginViewModel(get(), get(), get()) }
Expand Down
Loading

0 comments on commit 27fe90e

Please sign in to comment.