Skip to content

fix: support appium_lib_core 13.x snake_case capabilities#37

Merged
rishigupta1599 merged 2 commits into
mainfrom
fix/appium-lib-core-13-capabilities
Jun 25, 2026
Merged

fix: support appium_lib_core 13.x snake_case capabilities#37
rishigupta1599 merged 2 commits into
mainfrom
fix/appium-lib-core-13-capabilities

Conversation

@rishigupta1599

Copy link
Copy Markdown
Contributor

Problem

appium_lib_core 13.x returns the session capabilities as an Appium::Core::Base::Capabilities object whose keys are snake_case (platform_name, device_screen_size, platform_version, …) instead of the camelCase keys (platformName, deviceScreenSize, platformVersion) that 12.x and earlier returned.

This silently broke Percy on core 13.x:

  • MetadataResolver.resolve looked for platformName, didn't find it, and raised PlatformNotSupported.
  • os_name, os_version, device_name, orientation, and deviceScreenSize all read nil.
  • Net effect: the build was created but no snapshot was taken ("Snapshot command was not called").

Fix

Introduce two helpers on Percy::Metadata:

  • normalize_capability_key(key) — lowercases and strips _/: and the leading appium vendor prefix, so platformName, platform_name, PLATFORM_NAME and appium:platformName all normalize to platformname.
  • normalized_capabilities(driver) — coerces the capabilities object to a plain Hash (as_json/to_h) and builds a {normalized_key => value} view. First key wins, preserving the previous resolver precedence of platformName over platform_name.

The affected metadata reads (MetadataResolver, os_name, os_version, get_orientation, Android device_screen_size/_device_name, iOS device_name) now go through get_capability_value, which reads fresh on every call (no memoization — same call semantics as before).

The Android viewportRect / viewport reads are intentionally left unchanged — that path already falls back to driver.get_system_bars consistently across all appium versions, so it's out of scope for this fix.

Compatibility

  • ✅ Backward compatible with appium_lib_core ≤ 12.x (camelCase).
  • ✅ Forward compatible with appium_lib_core 13.x (snake_case).

Verification

  • All 15 spec files pass (specs/*.rb).
  • Real BrowserStack App Automate Percy build under appium_lib 16.3.0 (core 13.x) now takes the snapshot and finalizes green (previously failed with PlatformNotSupported / "Snapshot command was not called").

🤖 Generated with Claude Code

rishigupta1599 and others added 2 commits June 25, 2026 00:22
appium_lib_core 13.x returns the session capabilities as an
Appium::Core::Base::Capabilities object whose keys are snake_case
(e.g. "platform_name", "device_screen_size") rather than the camelCase
keys ("platformName", "deviceScreenSize") older versions returned.

This broke metadata resolution on core 13.x: MetadataResolver could not
find platformName and raised PlatformNotSupported, and os_name/os_version/
device_name/orientation/deviceScreenSize all read nil, so no snapshot was
taken.

Add Percy::Metadata.normalize_capability_key / normalized_capabilities to
look capabilities up in a casing- and prefix-insensitive way (camelCase,
snake_case, SCREAMING_CASE and the W3C "appium:" vendor prefix), and route
the affected metadata reads through it. Backward compatible with
appium_lib_core 12.x and earlier (camelCase) and forward compatible with
13.x (snake_case). First-key-wins preserves the previous resolver
precedence of platformName over platform_name.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address PR review feedback:
- Normalize the nested `desired` capabilities hash via Percy::Metadata.normalize_hash
  so its keys (including `device`) resolve regardless of camelCase/snake_case,
  not just an ad-hoc deviceName/device_name fallback.
- Add regression tests covering the appium_lib_core 13.x snake_case failure
  modes: normalize_capability_key, os_name/os_version (platform_version),
  Android device_screen_size and nested desired device_name, iOS device_name.
- Document that the Android viewportRect lookup is intentionally left as-is
  (it degrades to driver.get_system_bars consistently across versions) and that
  normalize_capability_key strips all colons by design.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rishigupta1599

Copy link
Copy Markdown
Contributor Author

Claude Code PR Review

PR: #37Head: 600dd5aReviewers: stack:code-reviewer

Summary

Adds a casing/prefix-insensitive capability lookup (normalize_capability_key / normalize_hash / normalized_capabilities / get_capability_value) to Percy::Metadata and routes the metadata reads in metadata.rb, android_metadata.rb, ios_metadata.rb, and metadata_resolver.rb through it, fixing appium_lib_core 13.x which returns snake_case capability keys (platform_name, device_screen_size, …) instead of camelCase. Without the fix, resolution raised PlatformNotSupported and no snapshot was taken.

Review Table

Priority Category Check Status Notes
High Security No hardcoded secrets or credentials Pass None introduced
High Security Authentication/authorization checks present N/A SDK metadata code, no authz surface
High Security Input validation and sanitization Pass Caps coerced to Hash defensively (as_json/to_h/`
High Security No IDOR — resource ownership validated N/A No resource access
High Security No SQL injection (parameterized queries) N/A No DB access
High Correctness Logic is correct, handles edge cases Pass First-key-wins preserves platformName>platform_name precedence; nil-safe
High Correctness Error handling is explicit, no swallowed exceptions Pass os_version rescue is intentional and pre-existing
High Correctness No race conditions or concurrency issues N/A No shared mutable state added
Medium Testing New code has corresponding tests Pass Added normalize_capability_key + snake_case regression tests (Android/iOS/metadata)
Medium Testing Error paths and edge cases tested Pass camelCase, snake_case, SCREAMING, appium: prefix, symbol key all covered
Medium Testing Existing tests still pass (no regressions) Pass All 15 spec files green (32 metadata assertions)
Medium Performance No N+1 queries or unbounded data fetching Pass driver.capabilities is a local property; no remote calls
Medium Performance Long-running tasks use background jobs N/A
Medium Quality Follows existing codebase patterns Pass Mirrors existing metadata helper style
Medium Quality Changes are focused (single concern) Pass Scoped to capability key resolution
Low Quality Meaningful names, no dead code Pass
Low Quality Comments explain why, not what Pass Added rationale for viewportRect retention + colon-strip
Low Quality No unnecessary dependencies added Pass No new deps

Findings

All reviewer findings were either addressed in follow-up commit 600dd5a or are intentional/non-blocking:

  • [Medium] android_metadata.rb viewportRect lookup left as capabilities.to_json[...] — Intentional and now documented in code. This path already degrades to driver.get_system_bars consistently across all appium_lib_core versions (the rect read yields a non-Hash, so the rect arithmetic rescues to nil and falls back), so it is out of scope for the snake_case fix. Addressed: clarifying comment added.
  • [Low] Nested desired hash not normalized (device key missed for snake_case)Fixed: _device_name now normalizes the nested desired hash via Percy::Metadata.normalize_hash, covering devicename and device regardless of casing.
  • [Low] No memoization in get_capability_value — Intentional (documented): reads fresh each call to match prior behaviour. driver.capabilities is a local property, not a remote round-trip, so the cost is negligible.
  • [Low] Test coverage gap for snake_case pathsFixed: added regression tests for normalize_capability_key, os_name/os_version (snake_case platform_version), Android device_screen_size and nested desired device_name, and iOS device_name.
  • [Info] normalize_capability_key strips all colons, not only the vendor-prefix oneAddressed: documented as intentional; safe for all known Appium capabilities.

Verification

  • All 15 spec files pass (6 new regression tests added).
  • Four real BrowserStack App Automate Percy builds under appium_lib 16.3.0 (core 13.x) take the snapshot and finalize green: #75, #76, #77, #78 (post-review-changes).

Verdict: PASS — correct, focused, backward-compatible fix with regression coverage; all review feedback addressed.

@rishigupta1599
rishigupta1599 merged commit 06d2004 into main Jun 25, 2026
9 checks passed
@rishigupta1599 rishigupta1599 mentioned this pull request Jun 25, 2026
rishigupta1599 added a commit that referenced this pull request Jun 25, 2026
Bump version to 1.0.1.

Supersedes the stray 1.0.0 (published 2024-10-28) that RubyGems resolves
as `latest` due to semver ordering but which is broken with real Appium
drivers (strict platformName lookup -> PlatformNotSupported -> empty build).
1.0.1 is cut from current main, so `gem install percy-appium-app` now
resolves to working code that also includes appium_lib_core 13.x support
(PR #37).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants