@cometchat/skills 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -17
  3. package/bin/install.js +389 -43
  4. package/package.json +19 -9
  5. package/skills/cometchat/SKILL.md +1066 -265
  6. package/skills/cometchat-android-v5/SKILL.md +120 -0
  7. package/skills/cometchat-android-v5-components/SKILL.md +430 -0
  8. package/skills/cometchat-android-v5-core/SKILL.md +468 -0
  9. package/skills/cometchat-android-v5-customization/SKILL.md +305 -0
  10. package/skills/cometchat-android-v5-extensions/SKILL.md +124 -0
  11. package/skills/cometchat-android-v5-features/SKILL.md +130 -0
  12. package/skills/cometchat-android-v5-placement/SKILL.md +276 -0
  13. package/skills/cometchat-android-v5-production/SKILL.md +94 -0
  14. package/skills/cometchat-android-v5-push/SKILL.md +620 -0
  15. package/skills/cometchat-android-v5-testing/SKILL.md +187 -0
  16. package/skills/cometchat-android-v5-theming/SKILL.md +166 -0
  17. package/skills/cometchat-android-v5-troubleshooting/SKILL.md +73 -0
  18. package/skills/cometchat-android-v6/SKILL.md +107 -0
  19. package/skills/cometchat-android-v6-builder-settings/SKILL.md +145 -0
  20. package/skills/cometchat-android-v6-compose-components/SKILL.md +202 -0
  21. package/skills/cometchat-android-v6-compose-customization/SKILL.md +280 -0
  22. package/skills/cometchat-android-v6-compose-placement/SKILL.md +213 -0
  23. package/skills/cometchat-android-v6-compose-theming/SKILL.md +224 -0
  24. package/skills/cometchat-android-v6-core/SKILL.md +354 -0
  25. package/skills/cometchat-android-v6-events/SKILL.md +212 -0
  26. package/skills/cometchat-android-v6-extensions/SKILL.md +191 -0
  27. package/skills/cometchat-android-v6-features/SKILL.md +130 -0
  28. package/skills/cometchat-android-v6-kotlin-components/SKILL.md +197 -0
  29. package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +268 -0
  30. package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +248 -0
  31. package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +202 -0
  32. package/skills/cometchat-android-v6-production/SKILL.md +187 -0
  33. package/skills/cometchat-android-v6-push/SKILL.md +274 -0
  34. package/skills/cometchat-android-v6-testing/SKILL.md +279 -0
  35. package/skills/cometchat-android-v6-troubleshooting/SKILL.md +249 -0
  36. package/skills/cometchat-angular-components/SKILL.md +706 -0
  37. package/skills/cometchat-angular-core/SKILL.md +444 -0
  38. package/skills/cometchat-angular-customization/SKILL.md +424 -0
  39. package/skills/cometchat-angular-features/SKILL.md +447 -0
  40. package/skills/cometchat-angular-patterns/SKILL.md +447 -0
  41. package/skills/cometchat-angular-placement/SKILL.md +685 -0
  42. package/skills/cometchat-angular-production/SKILL.md +415 -0
  43. package/skills/cometchat-angular-theming/SKILL.md +365 -0
  44. package/skills/cometchat-angular-troubleshooting/SKILL.md +329 -0
  45. package/skills/cometchat-astro-patterns/SKILL.md +3 -3
  46. package/skills/cometchat-components/SKILL.md +11 -0
  47. package/skills/cometchat-core/SKILL.md +11 -6
  48. package/skills/cometchat-customization/references/component-catalog.md +1 -1
  49. package/skills/cometchat-features/SKILL.md +176 -108
  50. package/skills/cometchat-flutter-v5/SKILL.md +167 -0
  51. package/skills/cometchat-flutter-v5-calls/SKILL.md +192 -0
  52. package/skills/cometchat-flutter-v5-conversations/SKILL.md +117 -0
  53. package/skills/cometchat-flutter-v5-core/SKILL.md +221 -0
  54. package/skills/cometchat-flutter-v5-customization/SKILL.md +227 -0
  55. package/skills/cometchat-flutter-v5-events/SKILL.md +155 -0
  56. package/skills/cometchat-flutter-v5-messages/SKILL.md +258 -0
  57. package/skills/cometchat-flutter-v5-production/SKILL.md +207 -0
  58. package/skills/cometchat-flutter-v5-push/SKILL.md +284 -0
  59. package/skills/cometchat-flutter-v5-theming/SKILL.md +160 -0
  60. package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +129 -0
  61. package/skills/cometchat-flutter-v5-users-groups/SKILL.md +151 -0
  62. package/skills/cometchat-flutter-v6/SKILL.md +445 -0
  63. package/skills/cometchat-flutter-v6-calls/SKILL.md +333 -0
  64. package/skills/cometchat-flutter-v6-components/SKILL.md +365 -0
  65. package/skills/cometchat-flutter-v6-conversations/SKILL.md +210 -0
  66. package/skills/cometchat-flutter-v6-core/SKILL.md +305 -0
  67. package/skills/cometchat-flutter-v6-customization/SKILL.md +666 -0
  68. package/skills/cometchat-flutter-v6-events/SKILL.md +216 -0
  69. package/skills/cometchat-flutter-v6-features/SKILL.md +406 -0
  70. package/skills/cometchat-flutter-v6-messages/SKILL.md +333 -0
  71. package/skills/cometchat-flutter-v6-migration/SKILL.md +381 -0
  72. package/skills/cometchat-flutter-v6-placement/SKILL.md +398 -0
  73. package/skills/cometchat-flutter-v6-production/SKILL.md +632 -0
  74. package/skills/cometchat-flutter-v6-theming/SKILL.md +260 -0
  75. package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +726 -0
  76. package/skills/cometchat-flutter-v6-users-groups/SKILL.md +216 -0
  77. package/skills/cometchat-ios/SKILL.md +323 -0
  78. package/skills/cometchat-ios-components/SKILL.md +651 -0
  79. package/skills/cometchat-ios-core/SKILL.md +822 -0
  80. package/skills/cometchat-ios-customization/SKILL.md +825 -0
  81. package/skills/cometchat-ios-features/SKILL.md +725 -0
  82. package/skills/cometchat-ios-placement/SKILL.md +750 -0
  83. package/skills/cometchat-ios-production/SKILL.md +730 -0
  84. package/skills/cometchat-ios-push/SKILL.md +582 -0
  85. package/skills/cometchat-ios-theming/SKILL.md +631 -0
  86. package/skills/cometchat-ios-troubleshooting/SKILL.md +983 -0
  87. package/skills/cometchat-native-bare-patterns/SKILL.md +470 -0
  88. package/skills/cometchat-native-components/SKILL.md +594 -0
  89. package/skills/cometchat-native-core/SKILL.md +407 -0
  90. package/skills/cometchat-native-customization/SKILL.md +594 -0
  91. package/skills/cometchat-native-expo-patterns/SKILL.md +485 -0
  92. package/skills/cometchat-native-features/SKILL.md +608 -0
  93. package/skills/cometchat-native-placement/SKILL.md +500 -0
  94. package/skills/cometchat-native-production/SKILL.md +694 -0
  95. package/skills/cometchat-native-push/SKILL.md +593 -0
  96. package/skills/cometchat-native-testing/SKILL.md +583 -0
  97. package/skills/cometchat-native-theming/SKILL.md +656 -0
  98. package/skills/cometchat-native-troubleshooting/SKILL.md +433 -0
  99. package/skills/cometchat-nextjs-patterns/SKILL.md +5 -5
  100. package/skills/cometchat-production/SKILL.md +60 -0
  101. package/skills/cometchat-react-router-patterns/SKILL.md +23 -21
@@ -0,0 +1,274 @@
1
+ ---
2
+ name: cometchat-android-v6-push
3
+ description: "CometChat Android UIKit v6 push notifications — FCM setup, chat/call notification handling, VoIP integration, and deep-linking"
4
+ license: "MIT"
5
+ compatibility: "Android 9.0+ (API 28); Kotlin 1.9+; com.cometchat:chatuikit-compose-android:6.x / com.cometchat:chatuikit-kotlin-android:6.x"
6
+ allowed-tools: "executeBash, readFile, fileSearch, listDirectory"
7
+ metadata:
8
+ author: "CometChat"
9
+ version: "3.0.0"
10
+ tags: "cometchat, android, push, fcm, firebase, voip, notifications, calls"
11
+ ---
12
+
13
+ > **Companion skills:** cometchat-android-v6-core (init/login), cometchat-android-v6-events (call events), cometchat-android-v6-builder-settings (calling config)
14
+
15
+ ## Purpose
16
+
17
+ Set up push notifications for CometChat v6 using Firebase Cloud Messaging (FCM), handle chat and call notifications, and integrate VoIP for background call handling.
18
+
19
+ ## Use this skill when
20
+
21
+ - Adding FCM push notifications for chat messages
22
+ - Handling incoming call notifications (foreground and background)
23
+ - Setting up VoIP with TelecomManager for native call UI
24
+ - Implementing deep-link navigation from notifications
25
+
26
+ ## Do not use this skill when
27
+
28
+ - Just initializing the SDK (use `cometchat-android-v6-core`)
29
+ - Working with UI components (use `cometchat-*-components`)
30
+
31
+ ## 1. FCM Setup
32
+
33
+ ### 1.1 Gradle Dependencies
34
+
35
+ ```kotlin
36
+ // In project-level build.gradle
37
+ plugins {
38
+ id("com.google.gms.google-services") version "4.4.2" apply false
39
+ }
40
+
41
+ // In app-level build.gradle.kts
42
+ plugins {
43
+ id("com.google.gms.google-services")
44
+ }
45
+
46
+ dependencies {
47
+ implementation(platform("com.google.firebase:firebase-bom:33.x.x"))
48
+ implementation("com.google.firebase:firebase-messaging")
49
+ }
50
+ ```
51
+
52
+ Add `google-services.json` to your app module's root directory.
53
+
54
+ ### 1.2 Initialize Firebase
55
+
56
+ ```kotlin
57
+ // In Application.onCreate()
58
+ FirebaseApp.initializeApp(this)
59
+ ```
60
+
61
+ ## 2. FCMService Implementation
62
+
63
+ > **Heads-up — this is a reference pattern.** The classes referenced below (`CometChatVoIP`, `CometChatVoIPConnectionService`, `FCMMessageDTO`, `FCMCallDto`, `FCMService`, `VoIPPermissionListener`, `CometChatVoIPUtils`, `FCMMessageNotificationUtils`) are NOT exported from `com.cometchat:chatuikit-{compose,kotlin}-android:6.x`. They live in the `master-app-jetpack` sample app — copy the relevant source files into your project, or use them as a guide for writing your own equivalents. The kit's only public push surface is `CometChatNotifications.registerPushToken(...)` / `unregisterPushToken(...)`; everything below is glue you control.
64
+
65
+ Pattern from `master-app-jetpack/fcm/FCMService.kt` (sample-app code, copy into your project):
66
+
67
+ ```kotlin
68
+ class FCMService : FirebaseMessagingService() {
69
+
70
+ companion object {
71
+ var fcmToken: String? = null
72
+ private set
73
+ }
74
+
75
+ override fun onNewToken(token: String) {
76
+ super.onNewToken(token)
77
+ fcmToken = token
78
+ // Store token for later use (e.g., SharedPreferences)
79
+ }
80
+
81
+ override fun onMessageReceived(message: RemoteMessage) {
82
+ super.onMessageReceived(message)
83
+
84
+ if (message.data.isEmpty()) return
85
+
86
+ val type = message.data["type"]?.lowercase()
87
+ when (type) {
88
+ "chat" -> handleChatNotification(message)
89
+ "call" -> handleCallNotification(message)
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ ### 2.1 Chat Notification Handling
96
+
97
+ ```kotlin
98
+ private fun handleChatNotification(message: RemoteMessage) {
99
+ val fcmMessageDTO = Gson().fromJson(
100
+ Gson().toJson(message.data),
101
+ FCMMessageDTO::class.java
102
+ )
103
+
104
+ // Mark as delivered for read receipts (only if SDK is initialized)
105
+ if (CometChatUIKit.isSDKInitialized()) {
106
+ CometChat.markAsDelivered(
107
+ fcmMessageDTO.tag!!.toLong(),
108
+ fcmMessageDTO.sender!!,
109
+ fcmMessageDTO.receiverType!!,
110
+ fcmMessageDTO.receiver!!
111
+ )
112
+ }
113
+
114
+ // Show notification if user is NOT in the same chat
115
+ val isUser = fcmMessageDTO.receiverType == CometChatConstants.RECEIVER_TYPE_USER
116
+ val uid = if (isUser) fcmMessageDTO.sender!! else fcmMessageDTO.receiver!!
117
+
118
+ if (uid != currentOpenChatId) {
119
+ // Build and show notification
120
+ FCMMessageNotificationUtils.showNotification(
121
+ this, fcmMessageDTO, intent,
122
+ NOTIFICATION_KEY_REPLY_ACTION,
123
+ NotificationCompat.CATEGORY_MESSAGE
124
+ )
125
+ }
126
+ }
127
+ ```
128
+
129
+ ### 2.2 Call Notification Handling
130
+
131
+ ```kotlin
132
+ private fun handleCallNotification(message: RemoteMessage) {
133
+ val sessionId = message.data["sessionId"]
134
+ val callAction = message.data["callAction"]
135
+
136
+ // CRITICAL: Check if SDK is initialized
137
+ if (!CometChatUIKit.isSDKInitialized()) {
138
+ return // Cannot handle call without SDK
139
+ }
140
+
141
+ // Check VoIP permissions
142
+ if (!CometChatVoIP.hasReadPhoneStatePermission(this) ||
143
+ !CometChatVoIP.hasManageOwnCallsPermission(this) ||
144
+ !CometChatVoIP.hasAnswerPhoneCallsPermission(this)) {
145
+ return
146
+ }
147
+
148
+ // Check phone account is enabled
149
+ CometChatVoIP.hasEnabledPhoneAccountForVoIP(this, object : VoIPPermissionListener {
150
+ override fun onPermissionsGranted() {
151
+ handleCallFlow(message)
152
+ }
153
+ override fun onPermissionsDenied(error: CometChatVoIPError?) {
154
+ // Cannot show VoIP UI
155
+ }
156
+ })
157
+ }
158
+ ```
159
+
160
+ ## 3. VoIP Integration
161
+
162
+ ### 3.1 Required Permissions
163
+
164
+ ```xml
165
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
166
+ <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
167
+ <uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
168
+ ```
169
+
170
+ ### 3.2 VoIP Call Flow
171
+
172
+ ```kotlin
173
+ private fun handleCallFlow(message: RemoteMessage) {
174
+ val callData = Gson().fromJson(
175
+ Gson().toJson(message.data),
176
+ FCMCallDto::class.java
177
+ )
178
+
179
+ // Initialize VoIP
180
+ CometChatVoIP.init(this, applicationInfo.loadLabel(packageManager).toString())
181
+
182
+ when (callData.callAction) {
183
+ "initiated" -> {
184
+ // Show incoming call UI (only if app is in background)
185
+ if (!isAppInForeground()) {
186
+ voipIncomingCall(callData)
187
+ }
188
+ // If foreground, UIKit's CometChatIncomingCall handles it
189
+ }
190
+ "cancelled", "unanswered" -> {
191
+ // End the native call UI if session matches
192
+ if (CometChatVoIPUtils.currentSessionId == callData.sessionId) {
193
+ CometChatVoIP.telecomManager?.endCall()
194
+ }
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ ### 3.3 Foreground vs Background Routing
201
+
202
+ | App State | Incoming Call Handler |
203
+ |---|---|
204
+ | Foreground | UIKit's `CometChatIncomingCall` via SDK call listener |
205
+ | Background | VoIP via `TelecomManager` + `CometChatVoIPConnectionService` |
206
+ | Killed | FCM wakes app, but SDK may not be initialized — cannot handle call |
207
+
208
+ ### 3.4 Busy Rejection
209
+
210
+ ```kotlin
211
+ private fun rejectCallWithBusyStatus(call: Call) {
212
+ CometChat.rejectCall(
213
+ call.sessionId,
214
+ CometChatConstants.CALL_STATUS_BUSY,
215
+ object : CometChat.CallbackListener<Call>() {
216
+ override fun onSuccess(rejectedCall: Call?) {
217
+ rejectedCall?.let {
218
+ CometChatEvents.emitCallEvent(CometChatCallEvent.CallRejected(it))
219
+ }
220
+ }
221
+ override fun onError(e: CometChatException) { }
222
+ }
223
+ )
224
+ }
225
+ ```
226
+
227
+ ## 4. AndroidManifest Registration
228
+
229
+ ```xml
230
+ <service
231
+ android:name=".fcm.FCMService"
232
+ android:exported="false">
233
+ <intent-filter>
234
+ <action android:name="com.google.firebase.MESSAGING_EVENT" />
235
+ </intent-filter>
236
+ </service>
237
+
238
+ <!-- VoIP Connection Service -->
239
+ <service
240
+ android:name=".voip.CometChatVoIPConnectionService"
241
+ android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
242
+ android:exported="true">
243
+ <intent-filter>
244
+ <action android:name="android.telecom.ConnectionService" />
245
+ </intent-filter>
246
+ </service>
247
+ ```
248
+
249
+ ## 5. WebSocket Connection Management
250
+
251
+ From `master-app-jetpack/Application.kt` — manage WebSocket connections based on app lifecycle:
252
+
253
+ ```kotlin
254
+ // When app comes to foreground
255
+ CometChat.connect(object : CometChat.CallbackListener<String?>() {
256
+ override fun onSuccess(s: String?) { /* connected */ }
257
+ override fun onError(e: CometChatException) { /* failed */ }
258
+ })
259
+
260
+ // When app goes to background
261
+ CometChat.disconnect(object : CometChat.CallbackListener<String?>() {
262
+ override fun onSuccess(s: String?) { /* disconnected */ }
263
+ override fun onError(e: CometChatException) { /* failed */ }
264
+ })
265
+ ```
266
+
267
+ ## Hard rules
268
+
269
+ - ALWAYS check `CometChatUIKit.isSDKInitialized()` before making SDK calls in FCM service — the app may have been killed
270
+ - NEVER show VoIP incoming call UI when app is in foreground — UIKit's `CometChatIncomingCall` handles foreground calls
271
+ - VoIP requires READ_PHONE_STATE, MANAGE_OWN_CALLS, and ANSWER_PHONE_CALLS permissions — check all three before handling calls
272
+ - `CometChat.markAsDelivered()` requires the SDK to be initialized — skip it if not
273
+ - For push token registration, use `CometChatNotifications.registerPushToken(...)` / `unregisterPushToken(...)` — that is the kit's only public push API. The `CometChatVoIP*` / `FCM*` classes shown above are sample-app glue (`master-app-jetpack`), not part of `chatuikit-{compose,kotlin}`
274
+ - Always manage WebSocket connections based on app lifecycle to avoid battery drain
@@ -0,0 +1,279 @@
1
+ ---
2
+ name: cometchat-android-v6-testing
3
+ description: "CometChat Android UIKit v6 testing — unit testing ViewModels, Compose UI testing, Espresso, Maestro, and CI configuration"
4
+ license: "MIT"
5
+ compatibility: "Android 9.0+ (API 28); Kotlin 1.9+; com.cometchat:chatuikit-compose-android:6.x / com.cometchat:chatuikit-kotlin-android:6.x"
6
+ allowed-tools: "executeBash, readFile, fileSearch, listDirectory"
7
+ metadata:
8
+ author: "CometChat"
9
+ version: "3.0.0"
10
+ tags: "cometchat, android, testing, junit, kotest, compose-testing, espresso, maestro"
11
+ ---
12
+
13
+ > **Companion skills:** cometchat-android-v6-extensions (ViewModel/DataSource architecture), cometchat-android-v6-compose-components, cometchat-android-v6-kotlin-components
14
+
15
+ ## Purpose
16
+
17
+ Test CometChat UIKit v6 components and ViewModels — unit tests for the shared core module, Compose UI tests, Espresso tests for Views, and end-to-end tests with Maestro.
18
+
19
+ ## Use this skill when
20
+
21
+ - Writing unit tests for CometChat ViewModels
22
+ - Writing Compose UI tests for CometChat components
23
+ - Writing Espresso tests for Kotlin Views components
24
+ - Setting up Maestro flows for end-to-end testing
25
+ - Configuring CI for CometChat test suites
26
+
27
+ ## Do not use this skill when
28
+
29
+ - Debugging runtime issues (use `cometchat-android-v6-troubleshooting`)
30
+ - Working with component APIs (use `cometchat-*-components`)
31
+
32
+ ## 1. Test Dependencies
33
+
34
+ From `chatuikit-compose/build.gradle.kts`:
35
+
36
+ ```kotlin
37
+ dependencies {
38
+ // Unit testing
39
+ testImplementation("junit:junit:4.13.2")
40
+ testImplementation("io.kotest:kotest-runner-junit5:5.x")
41
+ testImplementation("io.kotest:kotest-assertions-core:5.x")
42
+ testImplementation("io.kotest:kotest-property:5.x")
43
+ testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.x")
44
+ testImplementation("androidx.arch.core:core-testing:2.x")
45
+ testImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.x")
46
+ testImplementation("org.mockito:mockito-core:5.x")
47
+ testImplementation("org.mockito.kotlin:mockito-kotlin:5.x")
48
+
49
+ // Android instrumented testing
50
+ androidTestImplementation("androidx.test.ext:junit:1.x")
51
+ androidTestImplementation("androidx.test.espresso:espresso-core:3.x")
52
+ androidTestImplementation("androidx.compose.ui:ui-test-junit4")
53
+ androidTestImplementation(platform("androidx.compose:compose-bom:2024.x.x"))
54
+
55
+ // Debug-only Compose tooling
56
+ debugImplementation("androidx.compose.ui:ui-tooling")
57
+ debugImplementation("androidx.compose.ui:ui-test-manifest")
58
+ }
59
+ ```
60
+
61
+ Enable JUnit Platform for Kotest:
62
+
63
+ ```kotlin
64
+ android {
65
+ testOptions {
66
+ unitTests.all {
67
+ it.useJUnitPlatform()
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ ## 2. Unit Testing ViewModels
74
+
75
+ ViewModels live in `chatuikit-core` and are shared across both stacks. Test them with coroutines-test and Mockito:
76
+
77
+ ```kotlin
78
+ import kotlinx.coroutines.test.runTest
79
+ import org.mockito.kotlin.mock
80
+ import org.mockito.kotlin.whenever
81
+
82
+ class CometChatConversationsViewModelTest {
83
+
84
+ @Test
85
+ fun `loadConversations emits success state`() = runTest {
86
+ val mockDataSource = mock<ConversationListDataSource>()
87
+ // Setup mock responses...
88
+
89
+ val viewModel = CometChatConversationsViewModel(/* inject mocks */)
90
+
91
+ // Collect state and assert
92
+ viewModel.uiState.test {
93
+ val state = awaitItem()
94
+ // Assert state is success with expected data
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ ### 2.1 Testing Events
101
+
102
+ ```kotlin
103
+ import com.cometchat.uikit.core.events.CometChatEvents
104
+ import com.cometchat.uikit.core.events.CometChatMessageEvent
105
+
106
+ @Test
107
+ fun `emitting message event is received by collector`() = runTest {
108
+ val events = mutableListOf<CometChatMessageEvent>()
109
+
110
+ val job = launch {
111
+ CometChatEvents.messageEvents.collect { events.add(it) }
112
+ }
113
+
114
+ CometChatEvents.emitMessageEventSync(
115
+ CometChatMessageEvent.MessageSent(mockMessage, MessageStatus.SUCCESS)
116
+ )
117
+
118
+ advanceUntilIdle()
119
+ assertEquals(1, events.size)
120
+ job.cancel()
121
+ }
122
+ ```
123
+
124
+ ## 3. Compose UI Testing
125
+
126
+ ```kotlin
127
+ import androidx.compose.ui.test.junit4.createComposeRule
128
+ import androidx.compose.ui.test.onNodeWithText
129
+ import androidx.compose.ui.test.performClick
130
+
131
+ class CometChatConversationsTest {
132
+
133
+ @get:Rule
134
+ val composeTestRule = createComposeRule()
135
+
136
+ @Test
137
+ fun conversationsList_displaysTitle() {
138
+ composeTestRule.setContent {
139
+ CometChatTheme {
140
+ CometChatConversations()
141
+ }
142
+ }
143
+
144
+ // Assert UI elements
145
+ composeTestRule.onNodeWithText("Chats").assertExists()
146
+ }
147
+
148
+ @Test
149
+ fun conversationItem_clickNavigates() {
150
+ var clickedConversation: Conversation? = null
151
+
152
+ composeTestRule.setContent {
153
+ CometChatTheme {
154
+ CometChatConversations(
155
+ onItemClick = { clickedConversation = it }
156
+ )
157
+ }
158
+ }
159
+
160
+ // Interact and assert
161
+ // composeTestRule.onNodeWithText("User Name").performClick()
162
+ // assertNotNull(clickedConversation)
163
+ }
164
+ }
165
+ ```
166
+
167
+ ## 4. Espresso Testing (Kotlin Views)
168
+
169
+ ```kotlin
170
+ import androidx.test.espresso.Espresso.onView
171
+ import androidx.test.espresso.assertion.ViewAssertions.matches
172
+ import androidx.test.espresso.matcher.ViewMatchers.*
173
+ import androidx.test.ext.junit.rules.ActivityScenarioRule
174
+
175
+ class ConversationsActivityTest {
176
+
177
+ @get:Rule
178
+ val activityRule = ActivityScenarioRule(ConversationsActivity::class.java)
179
+
180
+ @Test
181
+ fun conversationsList_isDisplayed() {
182
+ onView(withId(R.id.conversations))
183
+ .check(matches(isDisplayed()))
184
+ }
185
+ }
186
+ ```
187
+
188
+ ## 5. Property-Based Testing with Kotest
189
+
190
+ The project uses Kotest for property-based tests (e.g., `MessageAdapterPropertyTest.kt`):
191
+
192
+ ```kotlin
193
+ import io.kotest.core.spec.style.FunSpec
194
+ import io.kotest.property.forAll
195
+ import io.kotest.property.Arb
196
+ import io.kotest.property.arbitrary.string
197
+
198
+ class BubbleFactoryKeyTest : FunSpec({
199
+
200
+ test("factory key format is always category_type") {
201
+ forAll(Arb.string(1..20), Arb.string(1..20)) { category, type ->
202
+ val key = BubbleFactory.getKey(category, type)
203
+ key == "${category}_${type}"
204
+ }
205
+ }
206
+ })
207
+ ```
208
+
209
+ ## 6. Maestro End-to-End Testing
210
+
211
+ The `master-app-jetpack/ai-testing/` directory contains Maestro test configurations:
212
+
213
+ ```yaml
214
+ # maestro/login_flow.yaml
215
+ appId: com.example.jetpackuikit
216
+ ---
217
+ - launchApp
218
+ - tapOn: "Login"
219
+ - inputText: "superhero1"
220
+ - tapOn: "Submit"
221
+ - assertVisible: "Chats"
222
+ ```
223
+
224
+ Run with:
225
+
226
+ ```bash
227
+ maestro test maestro/login_flow.yaml
228
+ ```
229
+
230
+ ## 7. CI Configuration
231
+
232
+ ### 7.1 GitHub Actions
233
+
234
+ ```yaml
235
+ name: Test
236
+ on: [push, pull_request]
237
+ jobs:
238
+ unit-tests:
239
+ runs-on: ubuntu-latest
240
+ steps:
241
+ - uses: actions/checkout@v4
242
+ - uses: actions/setup-java@v4
243
+ with:
244
+ java-version: '17'
245
+ distribution: 'temurin'
246
+ - run: ./gradlew testDebugUnitTest
247
+
248
+ instrumented-tests:
249
+ runs-on: macos-latest
250
+ steps:
251
+ - uses: actions/checkout@v4
252
+ - uses: actions/setup-java@v4
253
+ with:
254
+ java-version: '17'
255
+ distribution: 'temurin'
256
+ - uses: reactivecircus/android-emulator-runner@v2
257
+ with:
258
+ api-level: 34
259
+ script: ./gradlew connectedDebugAndroidTest
260
+ ```
261
+
262
+ ## 8. Test Organization
263
+
264
+ | Test Type | Location | Framework | What to Test |
265
+ |---|---|---|---|
266
+ | Unit tests | `chatuikit-core/src/test/` | JUnit + Kotest + Mockito | ViewModels, DataSources, Repositories, Events |
267
+ | Unit tests | `chatuikit-compose/src/test/` | JUnit + Kotest | BubbleFactory, style resolution |
268
+ | Compose UI tests | `chatuikit-compose/src/androidTest/` | Compose Test | Component rendering, interactions |
269
+ | Views UI tests | `chatuikit-kotlin/src/androidTest/` | Espresso | Component rendering, interactions |
270
+ | E2E tests | `master-app-*/ai-testing/` | Maestro | Full user flows |
271
+
272
+ ## Hard rules
273
+
274
+ - ALWAYS use `CometChatTheme {}` wrapper in Compose test `setContent` blocks — components depend on CompositionLocal values
275
+ - Use `runTest` from `kotlinx-coroutines-test` for testing coroutines and SharedFlow
276
+ - Use `useJUnitPlatform()` in test options for Kotest compatibility
277
+ - Mock DataSources and Repositories when unit testing ViewModels — do NOT make real SDK calls in tests
278
+ - CometChat SDK must NOT be initialized in unit tests — mock all SDK interactions
279
+ - For Compose UI tests, use `createComposeRule()` not `createAndroidComposeRule()` unless you need Activity context