[Chat][Feature]UI Test Infrasture with Send Button (#535)
This commit is contained in:
Родитель
2ece7bd9f8
Коммит
553ebe2577
|
@ -8,7 +8,7 @@ buildscript {
|
|||
ui_library_version_name = '1.1.0'
|
||||
ui_library_version_code = getVersionCode()
|
||||
|
||||
kotlin_version = '1.7.10'
|
||||
kotlin_version = '1.7.20'
|
||||
jacoco_version = '0.8.7'
|
||||
junit_version = '4.13.2'
|
||||
|
||||
|
@ -55,7 +55,7 @@ buildscript {
|
|||
|
||||
androidx_activity_compose_version = '1.5.1'
|
||||
compose_version = '1.2.1'
|
||||
kotlin_compiler_extension_version = '1.3.1'
|
||||
kotlin_compiler_extension_version = '1.3.2'
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
|
|
@ -73,6 +73,7 @@ dependencies {
|
|||
|
||||
implementation "androidx.compose.ui:ui:$compose_version"
|
||||
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
|
||||
|
||||
implementation "androidx.activity:activity-compose:$androidx_activity_compose_version"
|
||||
implementation "androidx.compose.foundation:foundation:$compose_version"
|
||||
implementation "androidx.compose.material:material:$compose_version"
|
||||
|
@ -81,6 +82,7 @@ dependencies {
|
|||
|
||||
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
|
||||
debugImplementation "androidx.compose.ui:ui-tooling-data:$compose_version"
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")
|
||||
|
||||
|
||||
// Next two are added as an workaround for not being able to preview for latest compose version
|
||||
|
@ -97,4 +99,5 @@ dependencies {
|
|||
androidTestImplementation "androidx.test.ext:junit:$androidx_junit_version"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$androidx_espresso_core_version"
|
||||
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
|
||||
androidTestImplementation "androidx.test:rules:$androidx_test_rules_version"
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.azure.android.communication.ui.chat
|
||||
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import com.azure.android.communication.ui.chat.mocking.TestChatSDK
|
||||
import com.azure.android.communication.ui.chat.mocking.TestContextProvider
|
||||
import com.azure.android.communication.ui.chat.utilities.TestHelper
|
||||
import kotlinx.coroutines.test.TestCoroutineScheduler
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import org.junit.After
|
||||
import org.junit.Rule
|
||||
|
||||
/**
|
||||
* Basic functionality required for our UI tests: UI elements, permissions, dependency injection.
|
||||
*/
|
||||
internal open class BaseUiTest {
|
||||
lateinit var chatSDK: TestChatSDK
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
var grantPermissionRule: GrantPermissionRule
|
||||
|
||||
private val basePermissionList = arrayOf(
|
||||
"android.permission.ACCESS_NETWORK_STATE"
|
||||
)
|
||||
|
||||
init {
|
||||
grantPermissionRule = GrantPermissionRule.grant(*basePermissionList)
|
||||
}
|
||||
|
||||
// Can't be @Before due to requiring a specific test scheduler.
|
||||
fun injectDependencies(scheduler: TestCoroutineScheduler) {
|
||||
val coroutineContextProvider = TestContextProvider(UnconfinedTestDispatcher(scheduler))
|
||||
chatSDK = TestChatSDK(coroutineContextProvider)
|
||||
|
||||
TestHelper.chatSDK = chatSDK
|
||||
TestHelper.coroutineContextProvider = coroutineContextProvider
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
TestHelper.chatSDK = null
|
||||
TestHelper.coroutineContextProvider = null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.azure.android.communication.ui.chat
|
||||
|
||||
import androidx.compose.ui.test.assert
|
||||
import androidx.compose.ui.test.hasText
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import com.azure.android.communication.ui.chat.presentation.ui.chat.UITestTags
|
||||
import com.azure.android.communication.ui.chat.redux.state.ChatStatus
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
internal class BottomBarUITest : BaseUiTest() {
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun testOnMessageSentInputViewIsCleared() = runTest {
|
||||
injectDependencies(testScheduler)
|
||||
|
||||
// launch composite
|
||||
chatSDK.setChatStatus(ChatStatus.INITIALIZED)
|
||||
launchChatComposite()
|
||||
|
||||
// type message
|
||||
val message = "hello"
|
||||
composeTestRule.onNodeWithTag(UITestTags.MESSAGE_INPUT_BOX).performTextInput(message)
|
||||
composeTestRule.onNodeWithTag(UITestTags.MESSAGE_INPUT_BOX).assert(hasText(message))
|
||||
|
||||
// send message
|
||||
composeTestRule.onNodeWithTag(UITestTags.MESSAGE_SEND_BUTTON).performClick()
|
||||
|
||||
// assert message is cleared after send
|
||||
composeTestRule.onNodeWithTag(UITestTags.MESSAGE_INPUT_BOX).assert(hasText(""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnMessageSentFailedInputViewIsNotCleared() = runTest {
|
||||
injectDependencies(testScheduler)
|
||||
|
||||
// launch composite
|
||||
chatSDK.setChatStatus(ChatStatus.INITIALIZATION)
|
||||
launchChatComposite()
|
||||
|
||||
// type message
|
||||
val message = "hello"
|
||||
composeTestRule.onNodeWithTag(UITestTags.MESSAGE_INPUT_BOX).performTextInput(message)
|
||||
composeTestRule.onNodeWithTag(UITestTags.MESSAGE_INPUT_BOX).assert(hasText(message))
|
||||
|
||||
// send message
|
||||
composeTestRule.onNodeWithTag(UITestTags.MESSAGE_SEND_BUTTON).performClick()
|
||||
|
||||
// assert message is cleared after send
|
||||
composeTestRule.onNodeWithTag(UITestTags.MESSAGE_INPUT_BOX).assert(hasText(message))
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.azure.android.communication.ui.chat
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.azure.android.communication.ui.chat
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.azure.android.communication.common.CommunicationTokenCredential
|
||||
import com.azure.android.communication.common.CommunicationTokenRefreshOptions
|
||||
import com.azure.android.communication.ui.chat.models.ChatCompositeJoinLocator
|
||||
import com.azure.android.communication.ui.chat.models.ChatCompositeRemoteOptions
|
||||
|
||||
// Helper functions that access internal UI chat API.
|
||||
// These must reside in `com.azure.android.communication.ui.chat`
|
||||
|
||||
internal fun launchChatComposite() {
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val chatComposite = ChatCompositeBuilder().build()
|
||||
val communicationTokenRefreshOptions = CommunicationTokenRefreshOptions({ "token" }, true)
|
||||
val communicationTokenCredential =
|
||||
CommunicationTokenCredential(communicationTokenRefreshOptions)
|
||||
val remoteOptions =
|
||||
ChatCompositeRemoteOptions(
|
||||
ChatCompositeJoinLocator(
|
||||
"19:lSNju7o5X9EYJInIIxkJQw1TMnllGMytNCtvhYCxvpE1@thread.v2",
|
||||
"https://acs-ui-dev.communication.azure.com/"
|
||||
),
|
||||
communicationTokenCredential,
|
||||
"test"
|
||||
)
|
||||
|
||||
chatComposite.launchTest(appContext, remoteOptions, null)
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.azure.android.communication.ui.chat.mocking
|
||||
|
||||
import com.azure.android.communication.ui.chat.models.ChatEventModel
|
||||
import com.azure.android.communication.ui.chat.models.MessageInfoModel
|
||||
import com.azure.android.communication.ui.chat.models.MessagesPageModel
|
||||
import com.azure.android.communication.ui.chat.redux.state.ChatStatus
|
||||
import com.azure.android.communication.ui.chat.service.sdk.ChatSDK
|
||||
import com.azure.android.communication.ui.chat.service.sdk.wrapper.CommunicationIdentifier
|
||||
import com.azure.android.communication.ui.chat.service.sdk.wrapper.SendChatMessageResult
|
||||
import com.azure.android.communication.ui.chat.utilities.CoroutineContextProvider
|
||||
import java9.util.concurrent.CompletableFuture
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.threeten.bp.OffsetDateTime
|
||||
|
||||
internal class TestChatSDK(
|
||||
coroutineContextProvider: CoroutineContextProvider = CoroutineContextProvider(),
|
||||
) : ChatSDK {
|
||||
private val coroutineScope = CoroutineScope(coroutineContextProvider.Default)
|
||||
|
||||
private var chatEventSharedFlow = MutableSharedFlow<ChatEventModel>()
|
||||
private var chatStatusStateFlow: MutableStateFlow<ChatStatus> =
|
||||
MutableStateFlow(ChatStatus.NONE)
|
||||
private val messagesSharedFlow: MutableSharedFlow<MessagesPageModel> = MutableSharedFlow()
|
||||
private val chatEventModelSharedFlow: MutableSharedFlow<ChatEventModel> = MutableSharedFlow()
|
||||
|
||||
fun setChatStatus(status: ChatStatus) {
|
||||
chatStatusStateFlow.value = status
|
||||
}
|
||||
|
||||
override fun initialization() {
|
||||
}
|
||||
|
||||
override fun destroy() {}
|
||||
|
||||
override fun requestPreviousPage() {
|
||||
}
|
||||
|
||||
override fun requestChatParticipants() {
|
||||
}
|
||||
|
||||
override fun startEventNotifications() {
|
||||
}
|
||||
|
||||
override fun stopEventNotifications() {
|
||||
}
|
||||
|
||||
override fun getChatStatusStateFlow(): StateFlow<ChatStatus> = chatStatusStateFlow
|
||||
|
||||
override fun getMessagesPageSharedFlow(): SharedFlow<MessagesPageModel> = messagesSharedFlow
|
||||
override fun getChatEventSharedFlow(): SharedFlow<ChatEventModel> = chatEventSharedFlow
|
||||
override fun sendMessage(messageInfoModel: MessageInfoModel): CompletableFuture<SendChatMessageResult> {
|
||||
val future = CompletableFuture<SendChatMessageResult>()
|
||||
// coroutine to make sure requests are not blocking
|
||||
coroutineScope.launch {
|
||||
future.complete(null)
|
||||
}
|
||||
return future
|
||||
}
|
||||
|
||||
override fun deleteMessage(id: String): CompletableFuture<Void> {
|
||||
val future = CompletableFuture<Void>()
|
||||
// coroutine to make sure requests are not blocking
|
||||
coroutineScope.launch {
|
||||
future.complete(null)
|
||||
}
|
||||
return future
|
||||
}
|
||||
|
||||
override fun editMessage(id: String, content: String): CompletableFuture<Void> {
|
||||
val future = CompletableFuture<Void>()
|
||||
// coroutine to make sure requests are not blocking
|
||||
coroutineScope.launch {
|
||||
future.complete(null)
|
||||
}
|
||||
return future
|
||||
}
|
||||
|
||||
override fun sendTypingIndicator(): CompletableFuture<Void> {
|
||||
val future = CompletableFuture<Void>()
|
||||
// coroutine to make sure requests are not blocking
|
||||
coroutineScope.launch {
|
||||
future.complete(null)
|
||||
}
|
||||
return future
|
||||
}
|
||||
|
||||
override fun sendReadReceipt(id: String): CompletableFuture<Void> {
|
||||
val future = CompletableFuture<Void>()
|
||||
// coroutine to make sure requests are not blocking
|
||||
coroutineScope.launch {
|
||||
future.complete(null)
|
||||
}
|
||||
return future
|
||||
}
|
||||
|
||||
override fun removeParticipant(communicationIdentifier: CommunicationIdentifier): CompletableFuture<Void> {
|
||||
val future = CompletableFuture<Void>()
|
||||
// coroutine to make sure requests are not blocking
|
||||
coroutineScope.launch {
|
||||
future.complete(null)
|
||||
}
|
||||
return future
|
||||
}
|
||||
|
||||
override fun fetchMessages(from: OffsetDateTime?) {}
|
||||
|
||||
private fun onChatEventReceived(infoModel: ChatEventModel) {
|
||||
coroutineScope.launch {
|
||||
chatEventModelSharedFlow.emit(infoModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T> completedFuture(f: () -> T): CompletableFuture<T> {
|
||||
return CompletableFuture<T>().also { it.complete(f.invoke()) }
|
||||
}
|
||||
|
||||
internal fun <T> completedFuture(res: T): CompletableFuture<T> {
|
||||
return CompletableFuture<T>().also { it.complete(res) }
|
||||
}
|
||||
|
||||
internal fun completedNullFuture(): CompletableFuture<Void> {
|
||||
return CompletableFuture<Void>().also { it.complete(null) }
|
||||
}
|
||||
|
||||
internal fun completedNullFuture(
|
||||
coroutineScope: CoroutineScope,
|
||||
f: suspend () -> Any,
|
||||
): CompletableFuture<Void> {
|
||||
val future = CompletableFuture<Void>()
|
||||
coroutineScope.launch {
|
||||
f.invoke()
|
||||
future.complete(null)
|
||||
}
|
||||
return future
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.azure.android.communication.ui.chat.mocking
|
||||
|
||||
import com.azure.android.communication.ui.chat.utilities.CoroutineContextProvider
|
||||
import kotlinx.coroutines.test.TestDispatcher
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
internal class TestContextProvider(testCoroutineDispatcher: TestDispatcher) :
|
||||
CoroutineContextProvider() {
|
||||
override val Main: CoroutineContext = testCoroutineDispatcher
|
||||
override val IO: CoroutineContext = testCoroutineDispatcher
|
||||
override val Default: CoroutineContext = testCoroutineDispatcher
|
||||
override val SingleThreaded: CoroutineContext = testCoroutineDispatcher
|
||||
}
|
|
@ -161,4 +161,18 @@ public class ChatComposite {
|
|||
showCompositeUI(context);
|
||||
}
|
||||
}
|
||||
|
||||
void launchTest(final Context context,
|
||||
final ChatCompositeRemoteOptions remoteOptions,
|
||||
final ChatCompositeLocalOptions localOptions) {
|
||||
chatContainer.start(context, remoteOptions, localOptions);
|
||||
showTestCompositeUI(context);
|
||||
}
|
||||
|
||||
private void showTestCompositeUI(final Context context) {
|
||||
final Intent launchIntent = new Intent(context, ChatCompositeActivity.class);
|
||||
launchIntent.putExtra(ChatCompositeActivity.KEY_INSTANCE_ID, instanceId);
|
||||
launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(launchIntent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import com.azure.android.communication.ui.chat.service.sdk.ChatSDKWrapper
|
|||
import com.azure.android.communication.ui.chat.service.sdk.ChatEventHandler
|
||||
import com.azure.android.communication.ui.chat.service.sdk.ChatFetchNotificationHandler
|
||||
import com.azure.android.communication.ui.chat.utilities.CoroutineContextProvider
|
||||
import com.azure.android.communication.ui.chat.utilities.TestHelper
|
||||
import com.jakewharton.threetenabp.AndroidThreeTen
|
||||
|
||||
internal class ChatContainer(
|
||||
|
@ -91,7 +92,7 @@ internal class ChatContainer(
|
|||
context: Context,
|
||||
) =
|
||||
ServiceLocator.getInstance(instanceId = instanceId).apply {
|
||||
addTypedBuilder { CoroutineContextProvider() }
|
||||
addTypedBuilder { TestHelper.coroutineContextProvider ?: CoroutineContextProvider() }
|
||||
|
||||
val messageRepository = MessageRepository.createListBackedRepository()
|
||||
|
||||
|
@ -108,7 +109,7 @@ internal class ChatContainer(
|
|||
|
||||
addTypedBuilder {
|
||||
ChatService(
|
||||
chatSDK = ChatSDKWrapper(
|
||||
chatSDK = TestHelper.chatSDK ?: ChatSDKWrapper(
|
||||
context = context,
|
||||
chatConfig = configuration.chatConfig!!,
|
||||
coroutineContextProvider = locate(),
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.azure.android.communication.ui.chat.presentation.ui.chat
|
||||
|
||||
internal class UITestTags {
|
||||
companion object {
|
||||
const val MESSAGE_INPUT_BOX = "MESSAGE_INPUT_BOX"
|
||||
const val MESSAGE_SEND_BUTTON = "MESSAGE_SEND_BUTTON"
|
||||
}
|
||||
}
|
|
@ -12,7 +12,9 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.azure.android.communication.ui.chat.R
|
||||
import com.azure.android.communication.ui.chat.models.MessageInfoModel
|
||||
import com.azure.android.communication.ui.chat.redux.action.Action
|
||||
import com.azure.android.communication.ui.chat.redux.action.ChatAction
|
||||
|
@ -32,12 +34,15 @@ internal fun BottomBarView(
|
|||
) {
|
||||
|
||||
MessageInputView(
|
||||
contentDescription = "Message Input Field",
|
||||
contentDescription = stringResource(R.string.azure_communication_ui_chat_message_input_view_content_description),
|
||||
messageInputTextState = messageInputTextState,
|
||||
postAction = postAction
|
||||
)
|
||||
|
||||
SendMessageButtonView("Send Message Button", chatStatus = chatStatus) {
|
||||
SendMessageButtonView(
|
||||
contentDescription = stringResource(R.string.azure_communication_ui_chat_message_send_button_content_description),
|
||||
chatStatus = chatStatus
|
||||
) {
|
||||
postAction(
|
||||
ChatAction.SendMessage(
|
||||
MessageInfoModel(
|
||||
|
|
|
@ -33,6 +33,8 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import com.azure.android.communication.ui.chat.presentation.ui.chat.UITestTags
|
||||
import com.azure.android.communication.ui.chat.redux.action.Action
|
||||
import com.azure.android.communication.ui.chat.redux.action.ChatAction
|
||||
|
||||
|
@ -83,6 +85,7 @@ internal fun MessageInput(
|
|||
.padding(6.dp)
|
||||
.heightIn(52.dp, maxInputHeight)
|
||||
.onFocusChanged { onTextFieldFocused(it.isFocused) }
|
||||
.testTag(UITestTags.MESSAGE_INPUT_BOX)
|
||||
.then(semantics),
|
||||
|
||||
value = textContent,
|
||||
|
|
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
|
@ -18,6 +19,7 @@ import androidx.compose.ui.semantics.semantics
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.azure.android.communication.ui.chat.R
|
||||
import com.azure.android.communication.ui.chat.presentation.ui.chat.UITestTags
|
||||
import com.azure.android.communication.ui.chat.redux.state.ChatStatus
|
||||
|
||||
@Composable
|
||||
|
@ -36,7 +38,7 @@ internal fun SendMessageButtonView(
|
|||
else
|
||||
painterResource(id = R.drawable.azure_communication_ui_chat_ic_fluent_send_message_button_20_filled_disabled)
|
||||
Box(
|
||||
modifier = Modifier.clickable {
|
||||
modifier = Modifier.testTag(UITestTags.MESSAGE_SEND_BUTTON).clickable {
|
||||
if (chatStatus == ChatStatus.INITIALIZED) {
|
||||
onClick()
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ internal class ChatServiceListener(
|
|||
}
|
||||
|
||||
coroutineScope.launch {
|
||||
chatService.getChatEventSharedFlow().collect {
|
||||
chatService.getChatEventSharedFlow()?.collect {
|
||||
handleInfoModel(it, dispatch)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.azure.android.communication.ui.chat.utilities
|
||||
|
||||
import com.azure.android.communication.ui.chat.service.sdk.ChatSDK
|
||||
|
||||
/**
|
||||
* This singleton provides a shared global state that our in-process tests (i.e. instrumented on-device unit tests)
|
||||
* may use to inject their own implementations of dependencies into the library.
|
||||
*/
|
||||
internal object TestHelper {
|
||||
/**
|
||||
* Allows injecting a custom [ChatSDK] implementation.
|
||||
* E.g. a test may inject an in-memory implementation to avoid hitting real servers and simulate remote events.
|
||||
*/
|
||||
var chatSDK: ChatSDK? = null
|
||||
|
||||
/**
|
||||
* Allows injecting custom set of dispatchers used by the store reducer and within the library.
|
||||
* Tests will pass along their TestDispatcher in order to sequence events properly.
|
||||
*/
|
||||
var coroutineContextProvider: CoroutineContextProvider? = null
|
||||
}
|
|
@ -12,6 +12,8 @@
|
|||
<string name="azure_communication_ui_chat_first_name_is_typing">%1$s is typing</string>
|
||||
<string name="azure_communication_ui_chat_two_names_are_typing">%1$s and %2$s are typing"</string>
|
||||
<string name="azure_communication_ui_chat_three_or_more_are_typing">%1$s, %2$s and %3$d %4$s are typing</string>
|
||||
<string name="azure_communication_ui_chat_message_input_view_content_description">Message Input Field</string>
|
||||
<string name="azure_communication_ui_chat_message_send_button_content_description">Send Message Button</string>
|
||||
<string name="azure_communication_ui_chat_joined_chat">%1$s joined the chat</string>
|
||||
<string name="azure_communication_ui_chat_left_chat">%1$s left the chat</string>
|
||||
<string name="azure_communication_ui_chat_topic_updated">Topic Updated: %1$s</string>
|
||||
|
|
Загрузка…
Ссылка в новой задаче