google / google/EarlGrey

SwiftUI ViewRepresentable and UIViewRepresentable is not correctly placed in element hierarchy.

Open
#1,909 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Objective-C
Stars
5.7k
Forks
737
Avg merge
1d 20h
Merged PRs (30d)
5

Description

# Background
Hi there! I'm a beginner and still learning about EarlGrey and how to use them.

I created a simple SwiftUI View as simple as this. And want to test them.

Screen Shot 2023-03-20 at 18 56 01

Here's the SwiftUI Code
```
struct RegisterView: View {

@EnvironmentObject var viewModel: RegisterViewModel

@State var ageText: String = ""
@State var nameText: String = ""

@State var isFormValid: Bool = false

var body: some View {
VStack {
Text("Welcome to SwiftUI")
.font(Font.title)
.fontWeight(.semibold)
Text("Register here!")
.multilineTextAlignment(.center)
.padding(.bottom, 16.0)

VStack(spacing: 16.0) {
BorderedTextField(placeholder: "Name",
textBind: $nameText,
errorTextBind: $viewModel.nameValidationError)
.onChange(of: nameText) { newValue in
self.viewModel.name = newValue.isEmpty ? nil : newValue
}
.accessibilityIdentifier("register.name.field")

BorderedTextField(placeholder: "Age",
textBind: $ageText,
errorTextBind: $viewModel.ageValidationError)
.onChange(of: ageText, perform: { newValue in
// Convert into Integers
self.viewModel.age = Int(newValue)
})
.accessibilityIdentifier("register.age.field")
.keyboardType(.numberPad)
Button {
// Submit action
} label: {
NavigationLink("Next") {
PhoneNumberView()
.environmentObject(viewModel)
}
.frame(maxWidth: .infinity)
.padding(8.0)
}
.disabled(!isFormValid)
.buttonStyle(.automatic)
.accessibilityIdentifier("register.button.next")
} // - Register Form
Text("RegisterViewModel")
Text("Name: \(viewModel.name ?? "nil")")
Text("Age: \(viewModel.age == nil ? "nil" : String(viewModel.age!))")
} // - Main Stack
.padding(.horizontal, 16.0)
.onReceive(self.viewModel.isFirstFormValid
.throttle(for: .milliseconds(500), scheduler: RunLoop.main, latest: true)
) { output in
self.isFormValid = output
}
}

}
```

Here's the BorderedTextField
```

struct BorderedTextField: View {
/// Placeholder localize string key
private var placeholder: LocalizedStringKey

/// Text binding.
@Binding
private var textBind: String

@Binding
private var errorTextBind: String?

/// Initialize a textfield with bordered pattern
/// - Parameters:
/// - placeholder: Localized Key for Placeholder.
/// - textBind: Text binding
init(placeholder: LocalizedStringKey,
textBind: Binding,
errorTextBind: Binding? = nil) {
self.id = id
self.placeholder = placeholder
self._textBind = textBind
self._errorTextBind = errorTextBind ?? Binding.constant(nil)
}

var body: some View {
VStack(alignment: .leading) {
TextField(placeholder, text: $textBind)
.padding()
.overlay(
RoundedRectangle(cornerRadius: 8.0)
.stroke()
)
.accessibilityIdentifier("borderedtextfield.textfield")
Text(errorTextBind ?? "")
.foregroundColor(Color.red)
.accessibilityIdentifier("borderedtextfield.errortext")
}
.accessibilityElement(children: .contain)
}
}
```
I used `accessibilityElement` to retain all the child `accessibilityIdentifier` so I can differentiate them by their `parents`. Thus a ViewHierarchy structure is important.

# Expectation
I hope that the View Hierarchy on EarlGrey is shown like what XCUIApplication does.
Screen Shot 2023-03-10 at 21 12 01

I thought my current accessibilityIdentifier structure using accessibilityElement can work on UITest. But I was wrong when I'm using EarlGrey.

# The Issue
After taking sometime looking at the View Hierarchy I discover this.

Screen Shot 2023-03-10 at 21 09 57

All `UIViewControllerRepresentable` and `UIViewRepresentable` somewhat being put outside of the View Hierarchy. This will eliminate my chance on using the `parent` identifier to get a certain component.

I checked with a `UIViewControllerRepresentable` and a `UIButton` wrapped inside `UIViewRepresentable`. It will put the View outside of the view hierarchy.

> And yes. I'm surprised, underneath Apple still use UIViewRepresentable on UITextField for SwiftUI TextField haha.

### Any help is really appreciated. Thank you!

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the SwiftUI example with EarlGrey and inspect how UIViewControllerRepresentable and UIViewRepresentable appear in the EarlGrey view hierarchy. Compare the accessibilityElement(children: .contain) structure with the reported hierarchy; done means representable views retain the expected parent-child relationships for accessibility identifiers.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
mobile-dev, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.