Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ import com.salesforce.androidsdk.auth.defaultBuildAccountName
import com.salesforce.androidsdk.auth.onAuthFlowComplete
import com.salesforce.androidsdk.config.BootConfig
import com.salesforce.androidsdk.config.LoginServerManager.LoginServer
import com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.OnlyShowAuthorizedHosts
import com.salesforce.androidsdk.config.RuntimeConfig.getRuntimeConfig
import com.salesforce.androidsdk.security.SalesforceKeyGenerator.getRandom128ByteKey
import com.salesforce.androidsdk.security.SalesforceKeyGenerator.getSHA256Hash
import com.salesforce.androidsdk.ui.LoginActivity.Companion.ABOUT_BLANK
Expand Down Expand Up @@ -156,6 +158,8 @@ open class LoginViewModel(val bootConfig: BootConfig) : ViewModel() {
internal val defaultTitleText: String
get() = if (loginUrl.value == ABOUT_BLANK) "" else selectedServer.value ?: ""

internal val serverPickerAddConnectionButtonVisible = !getRuntimeConfig(SalesforceSDKManager.getInstance().appContext).getBoolean(OnlyShowAuthorizedHosts)

/** Additional Auth Values used for login. */
open var additionalParameters = hashMapOf<String, String>()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
Expand All @@ -124,7 +123,9 @@ import com.salesforce.androidsdk.R.string.sf__server_url_default_custom_label
import com.salesforce.androidsdk.R.string.sf__server_url_default_custom_url
import com.salesforce.androidsdk.R.string.sf__server_url_save
import com.salesforce.androidsdk.accounts.UserAccount
import com.salesforce.androidsdk.accounts.UserAccountManager
import com.salesforce.androidsdk.app.SalesforceSDKManager
import com.salesforce.androidsdk.config.LoginServerManager
import com.salesforce.androidsdk.config.LoginServerManager.LoginServer
import com.salesforce.androidsdk.ui.LoginViewModel
import com.salesforce.androidsdk.ui.theme.hintTextColor
Expand All @@ -146,10 +147,21 @@ internal const val TEXT_SELECTION_ALPHA = 0.2f
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PickerBottomSheet(pickerStyle: PickerStyle) {
val viewModel: LoginViewModel = viewModel(factory = SalesforceSDKManager.getInstance().loginViewModelFactory)
val loginServerManager = SalesforceSDKManager.getInstance().loginServerManager
val userAccountManager = SalesforceSDKManager.getInstance().userAccountManager
val activity = LocalContext.current.getActivity()
TestablePickerBottomSheet(
pickerStyle = pickerStyle,
)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
@VisibleForTesting
internal fun TestablePickerBottomSheet(
pickerStyle: PickerStyle,
viewModel: LoginViewModel = viewModel(factory = SalesforceSDKManager.getInstance().loginViewModelFactory),
loginServerManager: LoginServerManager = SalesforceSDKManager.getInstance().loginServerManager,
userAccountManager: UserAccountManager = SalesforceSDKManager.getInstance().userAccountManager,
activity: FragmentActivity? = LocalContext.current.getActivity(),
) {
val onNewLoginServerSelected = { newSelectedServer: Any?, closePicker: Boolean ->
if (newSelectedServer != null && newSelectedServer is LoginServer) {
viewModel.showServerPicker.value = !closePicker
Expand Down Expand Up @@ -194,8 +206,9 @@ fun PickerBottomSheet(pickerStyle: PickerStyle) {
when (pickerStyle) {
PickerStyle.LoginServerPicker ->
PickerBottomSheet(
pickerStyle,
sheetState,
addButtonVisible = viewModel.serverPickerAddConnectionButtonVisible,
pickerStyle = pickerStyle,
sheetState = sheetState,
list = loginServerManager.loginServers,
selectedListItem = loginServerManager.selectedLoginServer,
onItemSelected = onNewLoginServerSelected,
Expand All @@ -206,8 +219,8 @@ fun PickerBottomSheet(pickerStyle: PickerStyle) {

PickerStyle.UserAccountPicker ->
PickerBottomSheet(
pickerStyle,
sheetState,
pickerStyle = pickerStyle,
sheetState = sheetState,
list = userAccountManager.authenticatedUsers,
selectedListItem = userAccountManager.currentUser,
onItemSelected = onUserAccountSelected,
Expand All @@ -228,6 +241,7 @@ internal fun PickerBottomSheet(
sheetState: SheetState,
list: List<Any>,
selectedListItem: Any?,
addButtonVisible: Boolean = true,
onItemSelected: (Any?, Boolean) -> Unit,
getValidServer: ((String) -> String?)? = null,
addNewLoginServer: ((String, String) -> Unit)? = null,
Expand Down Expand Up @@ -428,7 +442,7 @@ internal fun PickerBottomSheet(
)

// Add New Connection/Account Button
if (listItem == mutableList.last()) {
if (listItem == mutableList.last() && addButtonVisible) {
OutlinedButton(
onClick = {
when (pickerStyle) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.salesforce.androidsdk.rest

import android.accounts.Account
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.Intent
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
Expand Down Expand Up @@ -53,6 +54,10 @@ class ClientManagerMockTest {
every { packageName } returns "packageName"
every { sendBroadcast(any()) } just runs
every { externalCacheDir } returns null
every { filesDir } returns targetContext.filesDir
every { getSharedPreferences(any(), any()) } answers {
targetContext.getSharedPreferences(firstArg(), MODE_PRIVATE)
}
}

mockkObject(SalesforceSDKManager)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,18 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextClearance
import androidx.compose.ui.test.performTextInput
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.salesforce.androidsdk.R.string.sf__account_selector_text
import com.salesforce.androidsdk.R.string.sf__custom_url_button
import com.salesforce.androidsdk.R.string.sf__pick_server
import com.salesforce.androidsdk.accounts.UserAccountManager
import com.salesforce.androidsdk.config.LoginServerManager.LoginServer
import com.salesforce.androidsdk.ui.components.AddConnection
import com.salesforce.androidsdk.ui.components.PickerBottomSheet
import com.salesforce.androidsdk.ui.components.PickerStyle
import com.salesforce.androidsdk.ui.components.TestablePickerBottomSheet
import com.salesforce.androidsdk.ui.components.UserAccountMock
import io.mockk.mockk
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -97,6 +104,44 @@ class PickerBottomSheetTest {

// Google's recommended naming scheme for test is "thingUnderTest_TriggerOfTest_ResultOfTest"

// region Public API Tests

@OptIn(ExperimentalMaterial3Api::class)
@Test
fun pickerBottomSheet_publicApiUserAccountPicker_displaysUserAccountPicker() {
val userAccountManager = mockk<UserAccountManager>(relaxed = true)
composeTestRule.setContent {
TestablePickerBottomSheet(
pickerStyle = PickerStyle.UserAccountPicker,
userAccountManager = userAccountManager
)
}

val context = getInstrumentation().targetContext
val button = composeTestRule.onNode(hasText(context.getString(sf__account_selector_text)))

button.assertIsDisplayed()
}

@OptIn(ExperimentalMaterial3Api::class)
@Test
fun pickerBottomSheet_publicApiLoginServerPicker_displaysLoginServerPicker() {
val userAccountManager = mockk<UserAccountManager>(relaxed = true)
composeTestRule.setContent {
TestablePickerBottomSheet(
pickerStyle = PickerStyle.LoginServerPicker,
userAccountManager = userAccountManager
)
}

val context = getInstrumentation().targetContext
val button = composeTestRule.onNode(hasText(context.getString(sf__pick_server)))

button.assertIsDisplayed()
}

// endregion

// region Add Connection Tests
@Test
fun saveButton_RespondsTo_InputValidation() {
Expand Down Expand Up @@ -238,6 +283,38 @@ class PickerBottomSheetTest {
customListItem.onChildAt(0).assertIsSelected()
}

@OptIn(ExperimentalMaterial3Api::class)
@Test
fun serverList_AddButtonVisibleTrue_DisplaysAddNewConnectionButton() {
composeTestRule.setContent {
PickerBottomSheetTestWrapper(
pickerStyle = PickerStyle.LoginServerPicker,
addButtonVisible = true,
)
}

val context = getInstrumentation().targetContext
val button = composeTestRule.onNode(hasText(context.getString(sf__custom_url_button)))

button.assertIsDisplayed()
}

@OptIn(ExperimentalMaterial3Api::class)
@Test
fun serverList_AddButtonVisibleFalse_HidesAddNewConnectionButton() {
composeTestRule.setContent {
PickerBottomSheetTestWrapper(
pickerStyle = PickerStyle.LoginServerPicker,
addButtonVisible = false,
)
}

val context = getInstrumentation().targetContext
val button = composeTestRule.onNode(hasText(context.getString(sf__custom_url_button)))

button.assertDoesNotExist()
}

@OptIn(ExperimentalMaterial3Api::class)
@Test
fun selectedServer_UpdatesOn_UIServerSelection() {
Expand Down Expand Up @@ -369,17 +446,28 @@ internal fun PickerBottomSheetTestWrapper(
initialValue = SheetValue.Expanded,
skipHiddenState = false,
),
list: List<Any> = when(pickerStyle) {
list: List<Any> = when (pickerStyle) {
PickerStyle.LoginServerPicker -> serverList
PickerStyle.UserAccountPicker -> userList
},
selectedListItem: Any = list.first(),
onItemSelected: (Any?, Boolean) -> Unit = { _,_ -> },
addButtonVisible: Boolean = true,
onItemSelected: (Any?, Boolean) -> Unit = { _, _ -> },
getValidServer: ((String) -> String?)? = { _ -> "" },
addNewLoginServer: ((String, String) -> Unit)? = { _,_ -> },
addNewLoginServer: ((String, String) -> Unit)? = { _, _ -> },
removeLoginServer: ((LoginServer) -> Unit)? = { },
addNewAccount: (() -> Unit)? = { },
) {
PickerBottomSheet(pickerStyle, sheetState, list, selectedListItem, onItemSelected, getValidServer,
addNewLoginServer, removeLoginServer, addNewAccount)
}
PickerBottomSheet(
pickerStyle = pickerStyle,
sheetState = sheetState,
list = list,
selectedListItem = selectedListItem,
addButtonVisible = addButtonVisible,
onItemSelected = onItemSelected,
getValidServer = getValidServer,
addNewLoginServer = addNewLoginServer,
removeLoginServer = removeLoginServer,
addNewAccount = addNewAccount,
)
}
Loading