Skip to content

test(ui-automation): cover SwiftUI tab identifier surfacing (#442)#456

Open
vsolano9 wants to merge 1 commit into
getsentry:mainfrom
vsolano9:bugfix/issue-442-tab-accessibility-identifier
Open

test(ui-automation): cover SwiftUI tab identifier surfacing (#442)#456
vsolano9 wants to merge 1 commit into
getsentry:mainfrom
vsolano9:bugfix/issue-442-tab-accessibility-identifier

Conversation

@vsolano9

@vsolano9 vsolano9 commented Jul 1, 2026

Copy link
Copy Markdown

Summary

Adds regression coverage for the SwiftUI.Tab accessibility-identifier behavior described in #442, and documents (via the tests and this PR) what snapshot-ui can and cannot surface for native tabs.

No production behavior changes. normalizeNode already reads AXUniqueId/AXIdentifier when present; the tests lock in that a tab is still exposed for automation by role + label when the platform provides no identifier, and that an identifier is surfaced when it is present.

Background

#442 reports that an .accessibilityIdentifier(...) set on a SwiftUI.Tab does not appear on the native tab target emitted by snapshot-ui, and asks whether the identifier can be surfaced or whether this is expected SwiftUI/UIKit behavior.

I reproduced it on an iPhone 17 Pro simulator with a minimal app:

TabView(selection: $selectedTab) {
    SwiftUI.Tab("Wheels", systemImage: "arrow.trianglehead.2.clockwise", value: .wheels) {
        Text("Wheels screen").accessibilityIdentifier("screen_wheels")
    }
    .accessibilityLabel("Wheels")
    .accessibilityIdentifier("tab_wheels")
}

Running axe describe-ui against it, the tab node is:

{
  "role": "AXRadioButton",
  "role_description": "tab",
  "subrole": "AXTabButton",
  "AXLabel": "Wheels",
  "AXValue": 0,
  "AXUniqueId": null,        // <- no identifier
  "children": [ { "role": "AXImage", "AXUniqueId": "arrow.trianglehead.2.clockwise" } ]
}

The tab_wheels string is absent from the entire accessibility tree. In contrast, screen_wheels/screen_dashboard applied to the tab's content does propagate normally. So this is a SwiftUI/UIKit limitation: the identifier applied to a Tab is not forwarded to the underlying tab bar item's accessibility element. There is no identifier in the tree for snapshot-ui to surface.

Solution

Two focused unit tests in runtime-snapshot.test.ts:

  1. A SwiftUI.Tab-shaped node with no AXUniqueId (matching the reproduced AX shape, including the SF Symbol child image) is still exposed as a tab with its label and a tap action, and does not gain an invented identifier or leak the child image's symbol identifier.
  2. A tab node that does expose an AXUniqueId surfaces it as the element identifier, confirming the parser already forwards identifiers when the platform provides them.

The practical guidance for tab automation stays: target tabs by role=tab plus visible label, which already works.

Testing

  • npm run typecheck (after npm run generate:version) - pass
  • npm run lint - pass
  • npm run format:check - pass
  • npm run build - pass
  • npm test (vitest run) - 213 files, 2665 tests pass (includes the 2 new tests)
  • Manual reproduction on iPhone 17 Pro simulator with bundled AXe describe-ui, as shown above

Notes

  • This documents expected platform behavior rather than fixing a parsing bug, so I did not use a closing keyword. If a website docs note ("tab automation should use role + label because SwiftUI does not forward a Tab identifier to the tab bar item") would help, I am happy to follow up.
  • Test-only change, so no CHANGELOG.md entry was added.

…y#442)

A `.accessibilityIdentifier(...)` applied to a SwiftUI `Tab` is not
forwarded to the native tab bar item's accessibility element, so
describe-ui reports no AXUniqueId/AXIdentifier for the tab. Reproduced on
a simulator: the identifier string is absent from the entire AX tree,
while an identifier applied to the tab's content still propagates.

Add regression coverage for the snapshot behavior: the tab is still
exposed for automation by role + label without inventing an identifier or
leaking the tab's SF Symbol image identifier, and a tab identifier is
surfaced when the platform does provide one. No production behavior
changes; parsing already reads AXUniqueId/AXIdentifier when present.

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.

1 participant