React Native Best Practices
vercel-labs/openreview
A prioritized rule set for React Native and Expo apps covering list performance, animation, navigation, UI patterns, state, rendering, monorepos and configuration.
A skill your agent uses to verify a Compose semantics node's properties from a UI test using assertExists, assertDoesNotExist, assertIsDisplayed, assertIsNotDisplayed, assertIsDeactivated…
$ npx skills add skydoves/android-testing-skills --skill asserting-node-state-and-text -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install skydoves/android-testing-skills asserting-node-state-and-text --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/compose/assertions/asserting-node-state-and-text .claude/skills/asserting-node-state-and-text && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "asserting-node-state-and-text" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/assertions/asserting-node-state-and-text into .claude/skills/asserting-node-state-and-text/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asserting-node-state-and-text", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/skydoves/android-testing-skills/tree/main/compose/assertions/asserting-node-state-and-textType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add skydoves/android-testing-skills --skill asserting-node-state-and-text -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install skydoves/android-testing-skills asserting-node-state-and-text --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/compose/assertions/asserting-node-state-and-text .agents/skills/asserting-node-state-and-text && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "asserting-node-state-and-text" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/assertions/asserting-node-state-and-text into .agents/skills/asserting-node-state-and-text/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asserting-node-state-and-text", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add skydoves/android-testing-skills --skill asserting-node-state-and-text -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install skydoves/android-testing-skills asserting-node-state-and-text --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/compose/assertions/asserting-node-state-and-text .cursor/skills/asserting-node-state-and-text && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "asserting-node-state-and-text" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/assertions/asserting-node-state-and-text into .cursor/skills/asserting-node-state-and-text/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asserting-node-state-and-text", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/skydoves/android-testing-skills.git --path compose/assertions/asserting-node-state-and-text--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add skydoves/android-testing-skills --skill asserting-node-state-and-text -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install skydoves/android-testing-skills asserting-node-state-and-text --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/compose/assertions/asserting-node-state-and-text .gemini/skills/asserting-node-state-and-text && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "asserting-node-state-and-text" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/assertions/asserting-node-state-and-text into .gemini/skills/asserting-node-state-and-text/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asserting-node-state-and-text", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install skydoves/android-testing-skills asserting-node-state-and-textInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add skydoves/android-testing-skills --skill asserting-node-state-and-text -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/compose/assertions/asserting-node-state-and-text .github/skills/asserting-node-state-and-text && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "asserting-node-state-and-text" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/assertions/asserting-node-state-and-text into .github/skills/asserting-node-state-and-text/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asserting-node-state-and-text", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add skydoves/android-testing-skills --skill asserting-node-state-and-text -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install skydoves/android-testing-skills asserting-node-state-and-text --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/compose/assertions/asserting-node-state-and-text .opencode/skills/asserting-node-state-and-text && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "asserting-node-state-and-text" agent skill from https://github.com/skydoves/android-testing-skills/tree/main/compose/assertions/asserting-node-state-and-text into .opencode/skills/asserting-node-state-and-text/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "asserting-node-state-and-text", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
asserting-node-state-and-textA skill your agent uses to verify a Compose semantics node's properties from a UI test using assertExists, assertDoesNotExist, assertIsDisplayed, assertIsNotDisplayed, assertIsDeactivated…
Asserting Node State And Text is an agent skill from skydoves/android-testing-skills. Use this skill to verify a Compose semantics node's properties from a UI test using assertExists, assertDoesNotExist, assertIsDisplayed, assertIsNotDisplayed, assertIsDeactivated, assertIsEnabled, assertIsOn, assertIsOff, assertIsSelected, assertIsFocused, assertTextEquals, assertTextContains, assertContentDescriptionEquals, assertValueEquals, assertRangeInfoEquals, assertHasClickAction, plus the generic assert(matcher) escape hatch and the boolean isDisplayed() / isNotDisplayed() for waitUntil predicates. Covers…
Its SKILL.md is about 3.6k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Mobile. The repository describes itself as: ⚡️ A set of skills for Android testing: Compose UI, AndroidX Test, JVM unit tests, and ADB. The licence is Apache-2.0.
Read from SKILL.md and the folder at commit 8665ed5. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are kotlin).
From the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
developer.android.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Asserting Node State And Text loads about 3.6k tokens when it runs. Until then it costs about 237 tokens; SKILL.md has 975 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from skydoves/android-testing-skills at commit 8665ed5, republished under its Apache-2.0 licence (© skydoves). 975 words, ~3,629 tokens.
.claude/skills/asserting-node-state-and-text/SKILL.md (or your agent's skills folder).fetchSemanticsNodeOnce a finder resolves to a SemanticsNodeInteraction, the next step is asserting it. Compose ships a typed assertion for almost every semantic property; the generic assert(matcher) covers the rest. This skill picks the right assertion, distinguishes "exists" from "displayed", and shows when boolean predicates belong inside waitUntil.
assertIsDisplayed vs assertExists (one verifies on-screen, the other verifies presence in the tree).assert(matcher) for a property without a typed extension.node.config[...] directly instead of using a typed assertion.../../finders/finding-nodes-by-tag-text-content/SKILL.md../asserting-bounds-and-dimensions/SKILL.md.SemanticsMatcher — see ../../finders/composing-semantics-matchers/SKILL.md.../../synchronization/synchronizing-with-idle/SKILL.md.ComposeTestRule / ComposeUiTest. See ../../setup/configuring-test-dependencies/SKILL.md.onNode* / onAllNodes* / a navigator.assertIsDisplayed semantics, the node must be composed AND placed AND at least partially on-screen post-clip (Assertions.kt:30-39). 1. Pick by question type. Map the developer's question to one assertion call. Every API listed is in commonMain/.../Assertions.kt unless noted.
| Question | API | File:line |
|---|---|---|
| Is it in the tree? | assertExists(errorMessageOnFail) | SemanticsNodeInteraction.kt:124-127 |
| Is it gone? | assertDoesNotExist() | SemanticsNodeInteraction.kt:94-110 |
| Is it a SubcomposeLayout retained child? | assertIsDeactivated() | SemanticsNodeInteraction.kt:137-148 |
| Is it visible to the user? | assertIsDisplayed() / assertIsNotDisplayed() | Assertions.kt:32-53 |
| Is it interactive? | assertIsEnabled() / assertIsNotEnabled() | Assertions.kt:60-67 |
| Is the toggle on? | assertIsOn() / assertIsOff() / assertIsToggleable() | Assertions.kt:74-81, 103 |
| Is the radio/tab selected? | assertIsSelected() / assertIsNotSelected() / assertIsSelectable() | Assertions.kt:88-110 |
| Does it have focus? | assertIsFocused() / assertIsNotFocused() | Assertions.kt:117-124 |
| Does it equal these strings? | assertTextEquals(vararg, includeEditableText=true) | Assertions.kt:181-185 |
| Does it contain this string? | assertTextContains(value, substring=false, ignoreCase=false) | Assertions.kt:204-208 |
| Content description equals these? | assertContentDescriptionEquals(vararg) | Assertions.kt:139-141 |
| Content description contains this? | assertContentDescriptionContains(value, substring, ignoreCase) | Assertions.kt:158-163 |
| StateDescription equals X? | assertValueEquals(value) | Assertions.kt:216-217 |
| ProgressBar at this position? | assertRangeInfoEquals(rangeInfo) | Assertions.kt:225-227 |
| Has a click action? | assertHasClickAction() / assertHasNoClickAction() | Assertions.kt:234-243 |
| Anything else | assert(matcher, messagePrefixOnError = null) | Assertions.kt:254-267 |
2. assertIsDisplayed vs assertExists — pick deliberately. A node assertExists() if it is in the semantics tree (composed). A node assertIsDisplayed() if it is composed AND placed AND at least partially visible after clipping (Assertions.kt:30-39). For LazyColumn items not yet scrolled into view, assertExists() may fail (item not composed) or succeed (item composed but off-screen — depends on prefetch); assertIsDisplayed() is the right gate for "the user sees this".
3. Use the typed assertion, not fetchSemanticsNode().config[...]. Typed assertions delegate to the matcher library and produce framework-formatted errors that name the failed property and dump the node (Assertions.kt:254-267 — the generic assert builds buildGeneralErrorMessage(errorMessageOnFail, selector, node)). Direct config reads bypass that error reporting and miss the auto-attached node dump.
4. For collections, use the collection-typed assertions. assertCountEquals(expectedSize) for cardinality (Assertions.kt:276-292), assertAny(matcher) for "at least one matches" (fails on empty — Assertions.kt:300-312), assertAll(matcher) for "every node matches" (passes on empty — Assertions.kt:323-339).
5. For waitUntil predicates, use boolean APIs, not throwing assertions. isDisplayed() returns Boolean (Assertions.kt:351) — perfect for rule.waitUntil { node.isDisplayed() }. isNotDisplayed() is its inverse (Assertions.kt:361). Both throw if multiple nodes match the finder, but never on zero. Cross-reference: ../../synchronization/synchronizing-with-idle/SKILL.md.
6. For one-off properties without a typed assertion, use assert(matcher). Combine with prebuilt or composed matchers from ../../finders/composing-semantics-matchers/SKILL.md. The messagePrefixOnError lambda is for adding context when this assert is a precondition for a larger operation.
assertIsDisplayed over assertExists for user-facing checks// WRONG
@Test
fun submit_isVisibleAfterError() {
rule.setContent { CheckoutScreen(state = state) }
rule.onNodeWithTag(SubmitTag).assertExists()
}
// WRONG because: assertExists passes for nodes that are composed but off-screen, behind a
// dialog, or measured to zero size. The test does not prove the user actually sees the button.// RIGHT
@Test
fun submit_isVisibleAfterError() {
rule.setContent { CheckoutScreen(state = state) }
rule.onNodeWithTag(SubmitTag).assertIsDisplayed()
}assertIsOn over manual config reads// WRONG
@Test
fun darkMode_switch_isOn() {
rule.setContent { SettingsScreen() }
val node = rule.onNodeWithTag(DarkModeSwitchTag).fetchSemanticsNode()
assertEquals(ToggleableState.On, node.config[SemanticsProperties.ToggleableState])
}
// WRONG because: bypasses the framework's error reporting. Failure prints a JUnit assertEquals
// diff with no node dump, no selector description, no hint about which screen the failure
// happened on.// RIGHT
@Test
fun darkMode_switch_isOn() {
rule.setContent { SettingsScreen() }
rule.onNodeWithTag(DarkModeSwitchTag).assertIsOn()
}assertIsOn is assert(isOn()) (Assertions.kt:74); isOn() is expectValue(SemanticsProperties.ToggleableState, ToggleableState.On) (Filters.kt:61-62). The error names the property and dumps the node automatically.
assertTextEquals (vararg) handles merged Text + EditableText// production:
TextField(value = "hello", onValueChange = {}, label = { Text("Name") },
modifier = Modifier.testTag(NameFieldTag))
// test:
rule.onNodeWithTag(NameFieldTag).assertTextEquals("Name", "hello")assertTextEquals(vararg) matches the unordered set of SemanticsProperties.Text plus, by default, SemanticsProperties.EditableText (Assertions.kt:181-185 → Filters.kt:274-293). To exclude editable text from the comparison: assertTextEquals("Name", includeEditableText = false).
assertCountEquals instead of onAllNodes(...).onFirst().assertExists()// WRONG
rule.onAllNodesWithTag(ItemTag).onFirst().assertExists()
// WRONG because: only proves there is at least one item. Drift to zero items causes
// "no node matched" instead of a count error; drift to many is silently accepted.// RIGHT
rule.onAllNodesWithTag(ItemTag).assertCountEquals(3)assert(matcher) for properties without a typed assertion@Test
fun row_hasPriorityOne() {
rule.setContent { TaskList(tasks = tasks) }
rule.onNodeWithTag(TaskRowTag)
.assert(SemanticsMatcher.expectValue(PriorityKey, 1))
}PriorityKey is a custom SemanticsPropertyKey<Int> set via Modifier.semantics { priority = … }. See ../../finders/composing-semantics-matchers/SKILL.md for matcher composition.
assertAny (fails on empty) vs assertAll (passes on empty)// "at least one row is selected" — must have at least one row
rule.onAllNodesWithTag(RowTag).assertAny(isSelected())
// "every row is enabled" — accepts zero rows (passes vacuously)
rule.onAllNodesWithTag(RowTag).assertAll(isEnabled())assertAny throws AssertionError("Failed to assertAny … no node matched") on an empty collection (Assertions.kt:305-307). assertAll returns successfully on an empty collection (Assertions.kt:323-339). Pick deliberately.
isDisplayed() inside waitUntil// WRONG
rule.waitUntil { rule.onNodeWithTag(SnackbarTag).assertIsDisplayed(); true }
// WRONG because: assertIsDisplayed throws on the first poll where the snackbar isn't yet
// shown. waitUntil expects a boolean — predicates that throw will fail-fast.// RIGHT
rule.waitUntil(timeoutMillis = 2_000) {
rule.onAllNodesWithTag(SnackbarTag).fetchSemanticsNodes().isNotEmpty() &&
rule.onNodeWithTag(SnackbarTag).isDisplayed()
}
rule.onNodeWithTag(SnackbarTag).assertTextContains("Saved")isDisplayed() returns false when zero nodes match and only throws on multiple matches (Assertions.kt:343-351). Cross-reference: ../../synchronization/synchronizing-with-idle/SKILL.md for waitUntil vs mainClock.advanceTimeUntil.
assertIsDeactivated for SubcomposeLayout retained children// LookaheadLayout / SubcomposeLayout may retain a previously composed slot for reuse.
// To verify the slot is currently deactivated (kept, not active):
rule.onNodeWithTag(RetainedSlotTag).assertIsDeactivated()assertIsDeactivated fetches the node without skipping deactivated ones (SemanticsNodeInteraction.kt:137-148) and checks node.layoutInfo.isDeactivated.
assertIsOn, assertIsEnabled, assertTextEquals, …) over fetchSemanticsNode().config[...]. The framework's error message includes the selector, the node dump, and the failed clause.assertIsDisplayed() when the contract is "the user sees this"; MUST use assertExists() only when the contract is "this is in the semantics tree" (e.g. checking presence in unmerged tree without on-screen requirement).isDisplayed() / isNotDisplayed() inside waitUntil { … } blocks; MUST NOT call throwing assertIs* from inside waitUntil.assertCountEquals for collection cardinality; MUST NOT index [0] to imply "the only one".../../finders/composing-semantics-matchers/SKILL.md.node.config[...] directly. All property checks go through assertIs* / assertTextEquals / assert(matcher).assertIsDisplayed is used for user-visible checks; assertExists only when on-screen visibility is irrelevant.waitUntil { … } blocks use isDisplayed() / isNotDisplayed() / fetchSemanticsNodes().size — no throwing assertIs* calls inside.assertAny / assertAll choice matches the intended empty-collection semantics../gradlew :app:connectedDebugAndroidTest (device) or :app:testDebugUnitTest (host) passes.compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Assertions.kt — every assertIs* / assertTextEquals / assertContentDescriptionEquals / assertHasClickAction / assertCountEquals / assertAll / assertAny / isDisplayed / isNotDisplayed.compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt — assertExists, assertDoesNotExist, assertIsDeactivated, fetchSemanticsNode.compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Filters.kt — isOn, isEnabled, hasText, hasClickAction underlying matchers used by typed assertions.© skydoves, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in compose/assertions/asserting-node-state-and-text of skydoves/android-testing-skills.
Open the folder on GitHubat commit 8665ed5
Asserting Node State And Text next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Asserting Node State And Text this skillskydoves/android-testing-skills | 333 | — | ~3.6k | Automated safety check: Pass | Apache-2.0 | |
| React Native Best Practicesvercel-labs/openreview | 1.7k | 17 repos | ~1.1k | Automated safety check: Pass | MIT | |
| Swiftui Protwostraws/SwiftUI-Agent-Skill | 5k | 2 repos | ~1k | Automated safety check: Pass | MIT | |
| Ip As LogoKartikLabhshetwar/better-shot | 2.4k | 1 repos | ~4.3k | Automated safety check: Pass | MIT | |
| Compose Multiplatform Patternsmonta-app/ocpp-emulator | 179 | 5 repos | ~2k | Automated safety check: Pass | Apache-2.0 | |
| Aso Appstore Screenshotsadamlyttleapps/claude-skill-aso-appstore-screenshots | 1.8k | 1 repos | ~9.6k | Automated safety check: Pass | MIT |
vercel-labs/openreview
A prioritized rule set for React Native and Expo apps covering list performance, animation, navigation, UI patterns, state, rendering, monorepos and configuration.
twostraws/SwiftUI-Agent-Skill
Comprehensively reviews SwiftUI code for best practices on modern APIs, maintainability, and performance.
KartikLabhshetwar/better-shot
Generate extremely simple, cute, personified square character images with rounded heavy forms, two purposeful character colors, one solid background color, and a dominant lower-corner composition.
monta-app/ocpp-emulator
Compose Multiplatform and Jetpack Compose patterns for KMP projects — state management, navigation, theming, performance, and platform-specific UI.
adamlyttleapps/claude-skill-aso-appstore-screenshots
Generate high-converting App Store screenshots by analyzing your app's codebase, discovering core benefits, and creating ASO-optimized screenshot images using Nano Banana Pro.
AFK-surf/OpenBridge
Best practices and example-driven guidance for building SwiftUI views and components.
skydoves/android-testing-skills
A skill your agent uses to verify Compose layout measurements from a UI test using assertWidthIsEqualTo, assertHeightIsEqualTo, assertWidthIsAtLeast, assertHeightIsAtLeast…
skydoves/android-testing-skills
A skill your agent uses to render every Jetpack Compose @Preview as a screenshot on a real Android device or emulator and publish a browsable HTML catalog from CI.
skydoves/android-testing-skills
A skill your agent uses to capture visual artefacts from a device for test failures, golden image generation, QA repro, and demo videos.
skydoves/android-testing-skills
A skill your agent uses to pick the correct Compose UI test entry point.
skydoves/android-testing-skills
A skill your agent uses to pick which behaviors to cover in an Android test suite using Google's five-category state vocabulary plus the explicit "what NOT to test" list from…
skydoves/android-testing-skills
A skill your agent uses to drive Jetpack Compose UI from tests with the high-level action APIs that do not go through a gesture builder — performClick, performScrollTo, performScrollToIndex…
Categories
A skill your agent uses to verify a Compose semantics node's properties from a UI test using assertExists, assertDoesNotExist, assertIsDisplayed, assertIsNotDisplayed, assertIsDeactivated…. Asserting Node State And Text is an agent skill from skydoves/android-testing-skills. Use this skill to verify a Compose semantics node's properties from a UI test using assertExists, assertDoesNotExist, assertIsDisplayed, assertIsNotDisplayed, assertIsDeactivated, assertIsEnabled, assertIsOn, assertIsOff, assertIsSelected, assertIsFocused, assertTextEquals, assertTextContains, assertContentDescriptionEquals, assertValueEquals, assertRangeInfoEquals, assertHasClickAction, plus the generic assert(matcher) escape hatch and the boolean isDisplayed() / isNotDisplayed() for waitUntil predicates.
Asserting Node State And Text fits situations like: verify a Compose semantics nodes properties from a UI test using assertExists; assertDoesNotExist; assertIsDisplayed; assertIsNotDisplayed.
Run `npx skills add skydoves/android-testing-skills --skill asserting-node-state-and-text -a claude-code`. Or copy the skill folder (compose/assertions/asserting-node-state-and-text in skydoves/android-testing-skills) into .claude/skills/asserting-node-state-and-text in your project. Claude Code loads it when a task matches its description.
Run `npx skills add skydoves/android-testing-skills --skill asserting-node-state-and-text -a codex`. Or copy the skill folder (compose/assertions/asserting-node-state-and-text in skydoves/android-testing-skills) into .agents/skills/asserting-node-state-and-text in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add skydoves/android-testing-skills --skill asserting-node-state-and-text -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/asserting-node-state-and-text, .gemini/skills/asserting-node-state-and-text, .github/skills/asserting-node-state-and-text and .opencode/skills/asserting-node-state-and-text in your project.
SKILL.md names no scripts, command-line tools or credentials: Asserting Node State And Text is instructions for the agent only.
SKILL.md names 1 domain. As links in the text: developer.android.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Asserting Node State And Text is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 3.6k tokens (SKILL.md is roughly 15k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Asserting Node State And Text: React Native Best Practices (vercel-labs/openreview, 1.7k stars), Swiftui Pro (twostraws/SwiftUI-Agent-Skill, 5k stars), Ip As Logo (KartikLabhshetwar/better-shot, 2.4k stars) and Compose Multiplatform Patterns (monta-app/ocpp-emulator, 179 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
skydoves (a GitHub user) maintains it in skydoves/android-testing-skills, which has 333 GitHub stars. The repository holds 50 skills in this directory. The repository was last updated on May 25, 2026.
Source: skydoves/android-testing-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.