Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inspection of onTap gesture on custom Layout #303

Open
babbage opened this issue May 13, 2024 · 1 comment
Open

Inspection of onTap gesture on custom Layout #303

babbage opened this issue May 13, 2024 · 1 comment
Labels
help wanted Extra attention is needed

Comments

@babbage
Copy link
Contributor

babbage commented May 13, 2024

I have been using ViewInspector with good success, but seem to have become stuck on how to correctly inspect a view and wonder if you could help me get my thinking straight:

Given this view:

public struct PersonCardView: View {
    var data: PersonCardData
    let actions: TimelineEntryActions

    public var body: some View {
        PersonCardViewContent(data: data)
            .onTapGesture {
                actions.onTap()
            }
    }
}

public struct PersonCardViewContent: View {
    var data: PersonCardData
    static let minHeight: CGFloat = 250
    
    public var body: some View {
        PersonCardHStack {
            PersonCardTextBlock(data: data)

            if let image = data.image {
                PersonCardImage(image: image)
            }
        }
        .padding(EdgeInsets(
            top: 0,
            leading: TimelineViewMetrics.cardPaddingLeading,
            bottom: 0,
            trailing: 0
        ))
        .frame(minHeight: PersonCardView.minHeight)
        .cardBackground()
    }
}

I can successfully test that the onTap gesture is correctly wired up with this test:

    func testA10_viewIsCorrectlyWiredToAction_onTap() throws {
        // Setup actions to test
        let expectation = expectation(description: "Action called")
        let minimalData = PersonCardData(
            type: .undefined,
            timestamp: Date(),
            givenName: "",
            familyName: "",
            jobTitle: "",
            department: "",
            organization: "",
            image: nil
        )
        let actions = actions(expectation, onTap: true)
        
        // Inspect view
        let view = PersonCardView(data: minimalData, actions: actions)
        let target = try view.inspect().find(PersonCardViewContent.self)
        
        // Process under test
        try target.callOnTapGesture()
        
        // Expectation
        waitForExpectations(timeout: 0.1)
    }

However, the only reason that the view is structured with the separate PersonCardViewContent was to get the test to pass. The actual SwiftUI view I originally wanted to test was basically the same, but with the onTapGesture just attached directly to the contents of the body:

public struct PersonCardView: View {
    var data: PersonCardData
    let actions: TimelineEntryActions
    static let minHeight: CGFloat = 250
    
    public var body: some View {
        PersonCardHStack {
            PersonCardTextBlock(data: data)
            
            if let image = data.image {
                PersonCardImage(image: image)
            }
        }
        .padding(EdgeInsets(
            top: 0,
            leading: TimelineViewMetrics.cardPaddingLeading,
            bottom: 0,
            trailing: 0
        ))
        .frame(minHeight: PersonCardView.minHeight)
        .cardBackground()
        .onTapGesture {
            actions.onTap()
        }
    }
}

In this case, I tried using .find(PersonCardView.self) as the target, but that would not trigger the expectation as the onTapGesture is not on that view, it's on the first child of that view...

PersonCardHStack is a custom SwiftUI layout, and I am unsure how to target it or its contents. I am sure this is a key contributor. It seems like in the PRs that have previously been addressed that it is possible to now target contents of custom layouts, but I am clearly missing something.

I would appreciate any pointers, so I don't have to change my view structure just to make this action wiring testable.

@babbage babbage changed the title Inspection of onTap gesture Inspection of onTap gesture on custom Layout May 13, 2024
@babbage
Copy link
Contributor Author

babbage commented May 14, 2024

I have since modified the test to target the PersonCardViewContent by using:

let target = try view.inspect().find(PersonCardView.self).first!

This lets me make PersonCardViewContent no longer public, which is nice and what I'd prefer to do, since no other view should be instantiating that view. However, I can't use this approach with the originally preferred view structure... if I do, the target ends up being the PersonCardTextBlock and the test fails saying:

failed: caught error: "PersonCardTextBlock does not have 'onTapGesture' modifier"

@nalexn nalexn added the help wanted Extra attention is needed label Jun 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants