android-sdd 1.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 (176) hide show
  1. package/dist/index.js +143 -0
  2. package/package.json +27 -0
  3. package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
  4. package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
  5. package/skills/Android Platform/Configuration/SKILL.md +201 -0
  6. package/skills/Android Platform/Filesystem/SKILL.md +216 -0
  7. package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
  8. package/skills/Android Platform/Manifest/SKILL.md +226 -0
  9. package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
  10. package/skills/Android Platform/Resources/SKILL.md +234 -0
  11. package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
  12. package/skills/Android Platform/State Restoration/SKILL.md +210 -0
  13. package/skills/Architecture/Bounded Context/SKILL.md +207 -0
  14. package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
  15. package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
  16. package/skills/Architecture/Entity Design/SKILL.md +243 -0
  17. package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
  18. package/skills/Architecture/MVI/SKILL.md +224 -0
  19. package/skills/Architecture/MVVM/SKILL.md +198 -0
  20. package/skills/Architecture/Modularization/SKILL.md +194 -0
  21. package/skills/Architecture/Offline First/SKILL.md +249 -0
  22. package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
  23. package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
  24. package/skills/Architecture/State Management/SKILL.md +229 -0
  25. package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
  26. package/skills/Architecture/Use Case Design/SKILL.md +244 -0
  27. package/skills/Architecture/Value Object/SKILL.md +226 -0
  28. package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
  29. package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
  30. package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
  31. package/skills/Build System/Build Cache/SKILL.md +233 -0
  32. package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
  33. package/skills/Build System/Build Variant/SKILL.md +215 -0
  34. package/skills/Build System/Convention Plugin/SKILL.md +288 -0
  35. package/skills/Build System/Dependency Management/SKILL.md +261 -0
  36. package/skills/Build System/Gradle/SKILL.md +284 -0
  37. package/skills/Build System/Incremental Build/SKILL.md +199 -0
  38. package/skills/Build System/KAPT/SKILL.md +198 -0
  39. package/skills/Build System/KSP/SKILL.md +263 -0
  40. package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
  41. package/skills/Build System/Specialized/C++/SKILL.md +308 -0
  42. package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
  43. package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
  44. package/skills/Build System/Version Catalog/SKILL.md +304 -0
  45. package/skills/Concurrency/Background Processing/SKILL.md +185 -0
  46. package/skills/Concurrency/Channel/SKILL.md +207 -0
  47. package/skills/Concurrency/Coroutine/SKILL.md +200 -0
  48. package/skills/Concurrency/Flow/SKILL.md +179 -0
  49. package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
  50. package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
  51. package/skills/Concurrency/StateFlow/SKILL.md +175 -0
  52. package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
  53. package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
  54. package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
  55. package/skills/Core Language/DSL/SKILL.md +186 -0
  56. package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
  57. package/skills/Core Language/Immutability/SKILL.md +156 -0
  58. package/skills/Core Language/KMP/SKILL.md +182 -0
  59. package/skills/Core Language/Kotlin/SKILL.md +187 -0
  60. package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
  61. package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
  62. package/skills/Core Language/Serialization/SKILL.md +191 -0
  63. package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
  64. package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
  65. package/skills/Data Layer/DAO/SKILL.md +225 -0
  66. package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
  67. package/skills/Data Layer/DataStore/SKILL.md +264 -0
  68. package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
  69. package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
  70. package/skills/Data Layer/File Storage/SKILL.md +247 -0
  71. package/skills/Data Layer/Indexing/SKILL.md +184 -0
  72. package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
  73. package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
  74. package/skills/Data Layer/Migration/SKILL.md +243 -0
  75. package/skills/Data Layer/Paging/SKILL.md +264 -0
  76. package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
  77. package/skills/Data Layer/Room/SKILL.md +244 -0
  78. package/skills/Data Layer/SQLite/SKILL.md +255 -0
  79. package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
  80. package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
  81. package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
  82. package/skills/Dependency Injection/Koin/SKILL.md +282 -0
  83. package/skills/Developer Experience/Detekt/SKILL.md +272 -0
  84. package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
  85. package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
  86. package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
  87. package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
  88. package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
  89. package/skills/Media/Audio/SKILL.md +257 -0
  90. package/skills/Media/Camera/SKILL.md +229 -0
  91. package/skills/Media/CameraX/SKILL.md +295 -0
  92. package/skills/Media/ExoPlayer/SKILL.md +258 -0
  93. package/skills/Media/Video/SKILL.md +228 -0
  94. package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
  95. package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
  96. package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
  97. package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
  98. package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
  99. package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
  100. package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
  101. package/skills/Navigation/Navigation/SKILL.md +215 -0
  102. package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
  103. package/skills/Networking/API Contract/SKILL.md +220 -0
  104. package/skills/Networking/Authentication/SKILL.md +210 -0
  105. package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
  106. package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
  107. package/skills/Networking/Ktor/SKILL.md +219 -0
  108. package/skills/Networking/Multipart Upload/SKILL.md +213 -0
  109. package/skills/Networking/OkHttp/SKILL.md +193 -0
  110. package/skills/Networking/REST/SKILL.md +178 -0
  111. package/skills/Networking/Rate Limiting/SKILL.md +170 -0
  112. package/skills/Networking/Retrofit/SKILL.md +241 -0
  113. package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
  114. package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
  115. package/skills/Networking/WebSocket/SKILL.md +224 -0
  116. package/skills/Observability/Crash Reporting/SKILL.md +219 -0
  117. package/skills/Observability/Logging/SKILL.md +168 -0
  118. package/skills/Observability/Metrics/SKILL.md +227 -0
  119. package/skills/Observability/Structured Logging/SKILL.md +234 -0
  120. package/skills/Performance/ANR Prevention/SKILL.md +192 -0
  121. package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
  122. package/skills/Performance/App Startup/SKILL.md +183 -0
  123. package/skills/Performance/Baseline Profile/SKILL.md +205 -0
  124. package/skills/Performance/Battery Optimization/SKILL.md +192 -0
  125. package/skills/Performance/Benchmark/SKILL.md +182 -0
  126. package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
  127. package/skills/Performance/Compose Optimization/SKILL.md +187 -0
  128. package/skills/Performance/Heap Management/SKILL.md +184 -0
  129. package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
  130. package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
  131. package/skills/Performance/Rendering Performance/SKILL.md +205 -0
  132. package/skills/Performance/Startup Optimization/SKILL.md +219 -0
  133. package/skills/Security/Biometric/SKILL.md +224 -0
  134. package/skills/Security/Certificate Transparency/SKILL.md +158 -0
  135. package/skills/Security/Cryptography/SKILL.md +244 -0
  136. package/skills/Security/Encrypted Storage/SKILL.md +273 -0
  137. package/skills/Security/Frida Detection/SKILL.md +230 -0
  138. package/skills/Security/Hook Detection/SKILL.md +197 -0
  139. package/skills/Security/Keystore/SKILL.md +272 -0
  140. package/skills/Security/Network Security Config/SKILL.md +186 -0
  141. package/skills/Security/Obfuscation/SKILL.md +226 -0
  142. package/skills/Security/Proguard/SKILL.md +202 -0
  143. package/skills/Security/R8/SKILL.md +234 -0
  144. package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
  145. package/skills/Security/Root Detection/SKILL.md +220 -0
  146. package/skills/Security/Secure Networking/SKILL.md +220 -0
  147. package/skills/System Integration/AlarmManager/SKILL.md +182 -0
  148. package/skills/System Integration/App Widget/SKILL.md +182 -0
  149. package/skills/System Integration/Deep Link/SKILL.md +187 -0
  150. package/skills/System Integration/Foreground Service/SKILL.md +212 -0
  151. package/skills/System Integration/Notification/SKILL.md +237 -0
  152. package/skills/System Integration/WorkManager/SKILL.md +256 -0
  153. package/skills/System Integration/clipboard/SKILL.md +155 -0
  154. package/skills/System Integration/share-intent/SKILL.md +182 -0
  155. package/skills/Testing/Compose Testing/SKILL.md +296 -0
  156. package/skills/Testing/Espresso/SKILL.md +292 -0
  157. package/skills/Testing/Fake Data/SKILL.md +245 -0
  158. package/skills/Testing/Integration Testing/SKILL.md +288 -0
  159. package/skills/Testing/Mocking/SKILL.md +229 -0
  160. package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
  161. package/skills/Testing/UI Testing/SKILL.md +293 -0
  162. package/skills/Testing/Unit Testing/SKILL.md +309 -0
  163. package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
  164. package/skills/UI System/Compose/SKILL.md +296 -0
  165. package/skills/UI System/Compose Animation/SKILL.md +281 -0
  166. package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
  167. package/skills/UI System/Compose Navigation/SKILL.md +255 -0
  168. package/skills/UI System/Compose Performance/SKILL.md +274 -0
  169. package/skills/UI System/Design System/SKILL.md +217 -0
  170. package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
  171. package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
  172. package/skills/UI System/Loading Strategy/SKILL.md +254 -0
  173. package/skills/UI System/Material 3/SKILL.md +279 -0
  174. package/skills/UI System/RTL/SKILL.md +179 -0
  175. package/src/index.ts +182 -0
  176. package/tsconfig.json +19 -0
@@ -0,0 +1,182 @@
1
+ ---
2
+ name: share-intent
3
+ description: >
4
+ Sharing content from Android apps using Intent and ShareSheet.
5
+ Load this skill when sharing text, URLs, images, or files with other
6
+ apps, customizing the share sheet, receiving shared content,
7
+ or implementing the Android Sharesheet API.
8
+ ---
9
+
10
+ # Share Intent
11
+
12
+ ## Overview
13
+ Android's share system lets apps send and receive content through `Intent.ACTION_SEND`. The system displays a share sheet with compatible apps. Modern apps use the `ShareCompat` API or `rememberLauncherForActivityResult` in Compose for a cleaner integration.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Use `ShareCompat.IntentBuilder` — not raw `Intent` — for outgoing shares
20
+ - Always use `FileProvider` for sharing files — direct `file://` URIs are blocked on Android 7+
21
+ - Handle incoming share intents in `onNewIntent` as well as `onCreate`
22
+ - Validate incoming share content — treat it as untrusted input
23
+ - Use `ChooserIntent` to customize the share sheet title
24
+
25
+ ---
26
+
27
+ ## Sharing Text and URLs
28
+
29
+ ```kotlin
30
+ // ✅ Share plain text or URL
31
+ fun shareText(context: Context, text: String, title: String = "Share via") {
32
+ val intent = ShareCompat.IntentBuilder(context)
33
+ .setType("text/plain")
34
+ .setText(text)
35
+ .setChooserTitle(title)
36
+ .createChooserIntent()
37
+
38
+ context.startActivity(intent)
39
+ }
40
+
41
+ // ✅ Share URL with subject
42
+ fun shareUrl(context: Context, url: String, subject: String) {
43
+ val intent = ShareCompat.IntentBuilder(context)
44
+ .setType("text/plain")
45
+ .setText(url)
46
+ .setSubject(subject)
47
+ .setChooserTitle("Share link via")
48
+ .createChooserIntent()
49
+
50
+ context.startActivity(intent)
51
+ }
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Sharing Images
57
+
58
+ ```kotlin
59
+ // ✅ Share image via FileProvider
60
+ fun shareImage(context: Context, imageFile: File) {
61
+ val uri = FileProvider.getUriForFile(
62
+ context,
63
+ "${context.packageName}.fileprovider",
64
+ imageFile
65
+ )
66
+
67
+ val intent = ShareCompat.IntentBuilder(context)
68
+ .setType("image/jpeg")
69
+ .setStream(uri)
70
+ .setChooserTitle("Share image via")
71
+ .createChooserIntent()
72
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
73
+
74
+ context.startActivity(intent)
75
+ }
76
+ ```
77
+
78
+ ---
79
+
80
+ ## FileProvider Setup
81
+
82
+ ```xml
83
+ <!-- AndroidManifest.xml -->
84
+ <provider
85
+ android:name="androidx.core.content.FileProvider"
86
+ android:authorities="${applicationId}.fileprovider"
87
+ android:exported="false"
88
+ android:grantUriPermissions="true">
89
+ <meta-data
90
+ android:name="android.support.FILE_PROVIDER_PATHS"
91
+ android:resource="@xml/file_paths" />
92
+ </provider>
93
+ ```
94
+
95
+ ```xml
96
+ <!-- res/xml/file_paths.xml -->
97
+ <paths>
98
+ <cache-path name="cache" path="." />
99
+ <files-path name="files" path="." />
100
+ <external-cache-path name="external_cache" path="." />
101
+ </paths>
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Sharing in Compose
107
+
108
+ ```kotlin
109
+ // ✅ Share from Compose
110
+ @Composable
111
+ fun ShareButton(text: String) {
112
+ val context = LocalContext.current
113
+
114
+ Button(onClick = {
115
+ shareText(context, text)
116
+ }) {
117
+ Icon(Icons.Default.Share, contentDescription = null)
118
+ Spacer(Modifier.width(8.dp))
119
+ Text("Share")
120
+ }
121
+ }
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Receiving Shared Content
127
+
128
+ ```kotlin
129
+ // ✅ Handle incoming share in Activity
130
+ class MainActivity : ComponentActivity() {
131
+
132
+ override fun onCreate(savedInstanceState: Bundle?) {
133
+ super.onCreate(savedInstanceState)
134
+ handleIncomingShare(intent)
135
+ setContent { AppContent() }
136
+ }
137
+
138
+ override fun onNewIntent(intent: Intent) {
139
+ super.onNewIntent(intent)
140
+ handleIncomingShare(intent)
141
+ }
142
+
143
+ private fun handleIncomingShare(intent: Intent?) {
144
+ if (intent?.action != Intent.ACTION_SEND) return
145
+
146
+ when {
147
+ intent.type == "text/plain" -> {
148
+ val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: return
149
+ // handle text
150
+ }
151
+ intent.type?.startsWith("image/") == true -> {
152
+ val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM) ?: return
153
+ // handle image URI
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ // Register in manifest to receive shares
160
+ // <intent-filter>
161
+ // <action android:name="android.intent.action.SEND" />
162
+ // <category android:name="android.intent.category.DEFAULT" />
163
+ // <data android:mimeType="text/plain" />
164
+ // <data android:mimeType="image/*" />
165
+ // </intent-filter>
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Anti-Patterns
171
+
172
+ - Using `file://` URI directly in share intent — blocked on Android 7+ (FileUriExposedException)
173
+ - Not granting `FLAG_GRANT_READ_URI_PERMISSION` for FileProvider URIs — receiving app can't read
174
+ - Not validating incoming shared content — treat as untrusted
175
+ - Using `startActivity` without a chooser for text sharing — opens default app silently
176
+
177
+ ---
178
+
179
+ ## Related Skills
180
+ - `filesystem` — writing files to share via FileProvider
181
+ - `manifest` — intent filter for receiving shares
182
+ - `deep-link` — handling incoming intents
@@ -0,0 +1,296 @@
1
+ ---
2
+ name: compose-testing
3
+ description: >
4
+ Jetpack Compose UI testing APIs and patterns.
5
+ Load this skill when using ComposeTestRule, finding nodes by semantics,
6
+ testing animations, asserting accessibility properties, verifying
7
+ recomposition behavior, or debugging failing Compose tests.
8
+ ---
9
+
10
+ # Compose Testing
11
+
12
+ ## Overview
13
+ Compose Testing uses a semantic tree — a parallel representation of the UI — to find and interact with composables. Unlike Espresso's view hierarchy, Compose tests work through `SemanticsNode`s which expose accessibility properties. Tests are deterministic because `ComposeTestRule` controls the clock.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Tests interact through the **semantic tree** — not the visual layout
20
+ - **`testTag`** is the most reliable locator — use for non-text UI elements
21
+ - **`contentDescription`** serves double duty — accessibility and testability
22
+ - `ComposeTestRule` **controls the clock** — animations and async are deterministic
23
+ - Compose tests are **synchronous by default** — the rule awaits idle state after each action
24
+
25
+ ---
26
+
27
+ ## Test Rule Setup
28
+
29
+ ```kotlin
30
+ // ✅ For isolated composable tests (no Activity needed)
31
+ @get:Rule
32
+ val composeRule = createComposeRule()
33
+
34
+ // ✅ For full Activity/NavHost tests
35
+ @get:Rule
36
+ val composeRule = createAndroidComposeRule<MainActivity>()
37
+
38
+ // ✅ For Hilt + Activity tests
39
+ @HiltAndroidTest
40
+ class MyScreenTest {
41
+ @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this)
42
+ @get:Rule(order = 1) val composeRule = createAndroidComposeRule<MainActivity>()
43
+ }
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Semantic Matchers Reference
49
+
50
+ ```kotlin
51
+ // ── Text ──────────────────────────────────────────────────────────────────
52
+ hasText("Submit") // exact match
53
+ hasText("ubmit", substring = true) // substring
54
+ hasText("submit", ignoreCase = true) // case-insensitive
55
+
56
+ // ── Tags ──────────────────────────────────────────────────────────────────
57
+ hasTestTag("my_button")
58
+
59
+ // ── Accessibility ─────────────────────────────────────────────────────────
60
+ hasContentDescription("Back")
61
+ hasContentDescription("icon", substring = true)
62
+
63
+ // ── Role / Type ───────────────────────────────────────────────────────────
64
+ isDialog()
65
+ isPopup()
66
+ isFocused()
67
+ isEnabled()
68
+ isNotEnabled()
69
+ hasClickAction()
70
+ hasScrollAction()
71
+ isSelected()
72
+ isToggleable()
73
+ isOn()
74
+ isOff()
75
+
76
+ // ── Hierarchy ─────────────────────────────────────────────────────────────
77
+ hasParent(hasTestTag("card"))
78
+ hasAnyChild(hasText("Ali"))
79
+ hasAnySibling(hasText("Submit"))
80
+
81
+ // ── Combining ─────────────────────────────────────────────────────────────
82
+ hasText("Ali") and isEnabled()
83
+ hasText("Ali") or hasTestTag("ali_node")
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Finding Nodes
89
+
90
+ ```kotlin
91
+ // Single node — throws if 0 or 2+ found
92
+ composeRule.onNodeWithText("Submit")
93
+ composeRule.onNodeWithTag("loading_indicator")
94
+ composeRule.onNodeWithContentDescription("Close")
95
+ composeRule.onNode(hasText("Ali") and isEnabled())
96
+
97
+ // Multiple nodes
98
+ composeRule.onAllNodesWithText("Item")
99
+ composeRule.onAllNodesWithTag("list_item")
100
+ composeRule.onAllNodes(hasClickAction())
101
+
102
+ // Indexed access
103
+ composeRule.onAllNodesWithTag("list_item")[0]
104
+ composeRule.onAllNodesWithTag("list_item").onFirst()
105
+ composeRule.onAllNodesWithTag("list_item").onLast()
106
+ ```
107
+
108
+ ---
109
+
110
+ ## Actions
111
+
112
+ ```kotlin
113
+ val node = composeRule.onNodeWithTag("my_button")
114
+
115
+ // Click
116
+ node.performClick()
117
+ node.performLongClick()
118
+ node.performDoubleClick()
119
+
120
+ // Text input
121
+ node.performTextInput("Hello")
122
+ node.performTextClearance()
123
+ node.performTextReplacement("New text")
124
+
125
+ // Scroll
126
+ node.performScrollTo() // scroll this node into view
127
+ node.performScrollToIndex(10) // scroll LazyList to index
128
+ node.performScrollToKey("item_key") // scroll LazyList to keyed item
129
+ node.performScrollToNode(hasText("Target"))
130
+
131
+ // Gestures
132
+ node.performTouchInput { swipeLeft() }
133
+ node.performTouchInput { swipeUp() }
134
+ node.performTouchInput { click(center) }
135
+
136
+ // IME
137
+ node.performImeAction()
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Assertions
143
+
144
+ ```kotlin
145
+ val node = composeRule.onNodeWithTag("result_text")
146
+
147
+ // Existence
148
+ node.assertExists()
149
+ node.assertDoesNotExist()
150
+
151
+ // Visibility
152
+ node.assertIsDisplayed()
153
+ node.assertIsNotDisplayed()
154
+
155
+ // State
156
+ node.assertIsEnabled()
157
+ node.assertIsNotEnabled()
158
+ node.assertIsFocused()
159
+ node.assertIsSelected()
160
+ node.assertIsOn()
161
+ node.assertIsOff()
162
+
163
+ // Content
164
+ node.assertTextEquals("Exact text")
165
+ node.assertTextContains("partial")
166
+ node.assertContentDescriptionEquals("desc")
167
+ node.assertHasClickAction()
168
+
169
+ // Count
170
+ composeRule.onAllNodesWithTag("item").assertCountEquals(3)
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Testing State Changes
176
+
177
+ ```kotlin
178
+ // ✅ Test that UI updates when state changes
179
+ @Test
180
+ fun showsErrorAfterFailedLoad() {
181
+ var state: UserListUiState by mutableStateOf(UserListUiState.Loading)
182
+
183
+ composeRule.setContent {
184
+ AppTheme {
185
+ UserListContent(state = state, onUserClick = {})
186
+ }
187
+ }
188
+
189
+ // Initial loading state
190
+ composeRule.onNodeWithTag("loading_indicator").assertIsDisplayed()
191
+
192
+ // Update state
193
+ state = UserListUiState.Error("Network failed")
194
+
195
+ // Verify UI updated
196
+ composeRule.onNodeWithText("Network failed").assertIsDisplayed()
197
+ composeRule.onNodeWithTag("loading_indicator").assertDoesNotExist()
198
+ }
199
+ ```
200
+
201
+ ---
202
+
203
+ ## Clock Control and Animations
204
+
205
+ ```kotlin
206
+ // ✅ Disable auto-advance for manual clock control
207
+ composeRule.mainClock.autoAdvance = false
208
+
209
+ // Advance by specific duration
210
+ composeRule.mainClock.advanceTimeBy(300L)
211
+
212
+ // Advance until idle
213
+ composeRule.mainClock.advanceTimeUntilIdle()
214
+
215
+ // ✅ Test animated visibility
216
+ @Test
217
+ fun animatedContentAppearsAfterDelay() {
218
+ var visible by mutableStateOf(false)
219
+
220
+ composeRule.setContent {
221
+ AnimatedVisibility(visible = visible) {
222
+ Text("Hello", modifier = Modifier.testTag("animated_text"))
223
+ }
224
+ }
225
+
226
+ composeRule.onNodeWithTag("animated_text").assertDoesNotExist()
227
+
228
+ visible = true
229
+ composeRule.mainClock.advanceTimeBy(500L)
230
+
231
+ composeRule.onNodeWithTag("animated_text").assertIsDisplayed()
232
+ }
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Debugging Failing Tests
238
+
239
+ ```kotlin
240
+ // ✅ Print semantic tree to understand what's available
241
+ composeRule.onRoot().printToLog("TEST_TAG")
242
+
243
+ // ✅ Print subtree
244
+ composeRule.onNodeWithTag("user_card").printToLog("CARD_TREE")
245
+
246
+ // Output shows full semantic tree:
247
+ // Node #1 at (0.0, 0.0, 411.4, 72.7)px
248
+ // |-Node #2 at (16.0, 12.0, 395.4, 60.7)px
249
+ // Text = "Ali Rezaei"
250
+ // Actions = [OnClick, GetTextLayoutResult]
251
+ ```
252
+
253
+ ---
254
+
255
+ ## waitUntil
256
+
257
+ ```kotlin
258
+ // ✅ Wait for async UI change (max timeout)
259
+ composeRule.waitUntil(timeoutMillis = 5_000) {
260
+ composeRule
261
+ .onAllNodesWithTag("user_item")
262
+ .fetchSemanticsNodes()
263
+ .isNotEmpty()
264
+ }
265
+
266
+ // ✅ Wait for node to appear
267
+ composeRule.waitUntilAtLeastOneExists(
268
+ hasTestTag("user_item"),
269
+ timeoutMillis = 5_000
270
+ )
271
+
272
+ // ✅ Wait for node to disappear
273
+ composeRule.waitUntilDoesNotExist(
274
+ hasTestTag("loading_indicator"),
275
+ timeoutMillis = 5_000
276
+ )
277
+ ```
278
+
279
+ ---
280
+
281
+ ## Anti-Patterns
282
+
283
+ - Using `Thread.sleep()` for async waits — use `waitUntil` or let the test rule handle idle
284
+ - Locating nodes by position index in UI — fragile; use `testTag` or content
285
+ - Not wrapping test content in `AppTheme` — may fail if theme-dependent assertions are used
286
+ - Asserting on implementation-level composable names — use semantic properties instead
287
+ - `autoAdvance = false` without remembering to advance — test hangs indefinitely
288
+
289
+ ---
290
+
291
+ ## Related Skills
292
+ - `ui-testing` — full screen UI tests with Activity
293
+ - `compose` — Compose fundamentals including `testTag` placement
294
+ - `snapshot-testing` — screenshot-based regression testing
295
+ - `espresso` — View-based UI testing
296
+ - `accessibility` — semantic properties used in both testing and accessibility