Not possible to get Marked("Parent").Marked("Child")

Trying to get a specific child view for a specific parent using app.Query.
It's not possible to get it with Id / Marked / Text / Button methods.
Only the Raw is responding properly. The Class method is responding properly also.

In the sample, we try to get the Button of Id "Button" for the view of Id "UserInfoCell"
Part of the tree is:

                [Xamarin_Forms_Platform_iOS_ViewCellRenderer_ViewTableCell] id: "UserInfoCell"
                [UITableViewCellContentView]
                  [Xamarin_Forms_Platform_iOS_RendererFactory_DefaultRenderer]
                    [Xamarin_Forms_Platform_iOS_RendererFactory_DefaultRenderer]
                      [Xamarin_Forms_Platform_iOS_LabelRenderer]
                        [UILabel] label: "John Smith",  text: "John Smith"
                      [Xamarin_Forms_Platform_iOS_LabelRenderer]
                        [UILabel] label: "[email protected]",  text: "[email protected]"
                    [MG_iOS_Pages_Controls_MGButtonRenderer]
                      [UIButton] id: "Button",  label: "Logout"
                        [UIButtonLabel] label: "Logout",  text: "Logout"
                  [UIImageView]
                [UITableTextAccessibilityElement] id: "UserInfoCell",  label: "John Smith, [email protected]"
                [UIAccessibilityElementMockView] id: "Button",  label: "Logout"

If we try to use the following queries, there is no result returned:
app.Query(c => c.Marked("UserInfoCell").Marked("Button"))
app.Query(c => c.Id("UserInfoCell").Id("Button"))

The Class method, is working properly to get children:
app.Query(c => c.Id("UserInfoCell").Class("UIButton"))

And if we use the Raw method with Calabash style, it works, we have the view returned
app.Query(c => c.Raw("view marked:'UserInfoCell' view marked:'Button'"))
app.Query(c => c.Raw("view id:'UserInfoCell' view id:'Button'"))

Do you have any clue about this issue with Marked / Id calls VS Raw calls?
Thanks

Posts

  • PhucThaiPhucThai USMember
    edited March 2015

    As tree showed,

    [Xamarin_Forms_Platform_iOS_ViewCellRenderer_ViewTableCell] id: "UserInfoCell"

    and

    [UITableViewCellContentView]

    are sibling.

    Please try this

    app.Query( c =>
    c.Id("UserInfoCell").Sibling().Index(0) //[UITableViewCellContentView]
    .Child().Index(0) //[Xamarin_Forms_Platform_iOS_RendererFactory_DefaultRenderer]
    .Child("MG_iOS_Pages_Controls_MGButtonRenderer") //[MG_iOS_Pages_Controls_MGButtonRenderer]
    .Child("UIButton").Id("Button"); //[UIButton] id: "Button", label: "Logout"

    Just try to query, not test yet.

  • AlisonFernandesAlisonFernandes USMember ✭✭

    You have Parent/Sibling/Child methods you can use to achieve that, that being said, going down a tree is unreliable and defeats the element fetching purpose (you're calabash tree analysing / app compilation dependent), IMO you should find something that uniquely identifies your element, if possible change it's ID in the app (in iOS, set accessibilityIdentifier).

    Given that tree, I would go for the Class (or Class + Id)

    app.Query(c => c.Class("UIButton"))
    app.Query(c => c.Class("UIButton").Id("Button"))

    Nonetheless, the best solution would be to change the button ID to something like LogoutButton and just fetch by ID:

    app.Query(c => c.Id("LogoutButton"))

  • PhucThaiPhucThai USMember
    edited March 2015

    In the sample, we try to get the Button of Id "Button" for the view of Id "UserInfoCell"

    Does it means that he wants to start from Id "UserInfoCell" ?

    Is there any better way?

    Nonetheless, the best solution would be to change the button ID to something like LogoutButton and just fetch by ID:

    This is not suitable for one who is writing test only, just test on other apps

  • LudovicThomasLudovicThomas USMember ✭✭

    Thanks to both of you.
    For @PhucThai, yes the goal is the get the view that has the Id "Button" that is inside the View that has for Id "UserInfoCell".

    The view that has for ID "UserInfoCell" in that screen is in fact a generic cell, used in several places in our code. It's a ButtonCell, that has a title, a subtitle, and a button. The button has always the Id "Button"because it can be any call to action.

    The goal, is to not use any "Index" method, or any "Class" because we want to tests to be portable on both iOS and Android, without having to change class for each one.

    I can achieve to get the result with the Raw query, with the following one:
    app.Query(c => c.Raw("view id:'UserInfoCell' view id:'Button'"))

    But I don't understand why that one is not working:
    app.Query(c => c.Id("UserInfoCell").Id("Button"))

  • AlisonFernandesAlisonFernandes USMember ✭✭
    edited March 2015

    @PhucThai Exactly, whenever possible, as in, when you can change the code.

    @LudovicThomas The problem is, when you do Class("Class1").Id("Id1") for instance, you're querying for an element of Class1 with ID Id1, similar to a SQL query WHERE Class='Class1' AND Id='Id1', therefore, that line doesn't work because you're looking for an element that has the Id='UserInfoCell' *AND* Id='Button', which isn't exactly what you want right? :) You need tell the query to look up or down the hierarchy tree, using the Parent(top), Sibling (same level), Child (bottom) methods. In your specific case, you should use Child. That being said, as I don't use raw queries, I can't explain to you why it works but if you check your device logs, you'll find the requests the TestServer is receiving, enabling you to actually see what's being done differently under the hood, if you really want.

    To be fair, index or not, both are wrong approaches being used to solve a bigger problem, your native tests will never be fully portable - in my experience they will only be in the best case scenario, which is, a app being fully developed having UITest in mind, both platforms sharing equal ID's, other than that, you can simply forget it lol -, thus, your code structure should support that. How? Check this.

    In particular, IScreenQueries.cs, iOSQueries.cs and AndroidQueries.cs

  • PhucThaiPhucThai USMember

    @LudovicThomas

    The goal, is to not use any "Index" method, or any "Class" because we want to tests to be portable on both iOS and Android, without having to change class for each one.

    You could consider using Page Object Model pattern.

  • LudovicThomasLudovicThomas USMember ✭✭

    Again, thanks both for the feedback.

    @AlisonFernandes
    I understand that the behaviour is a "AND" and not a children, and that is the issue I'm opening there.
    Because, the class method is working with children, so the following code: app.Query(c => c.Id("Id1").Class("Class1")) will returns the list of children of the object Id1, that are of the class Class1.

    But the reverse code app.Query(c => c.Class("Class1").Id("Id1")) will returns 0, even if there is a child object that have the Id1 inside a parent of a class Class1.

    The raw query is working, because it's the way Calabash query is working. It's using directions as explained here: http://developer.xamarin.com/guides/testcloud/calabash/calabash-query-syntax/#Direction.

    The question is really why the behaviour is different between Id then Class that is taking care of children, and Class then Id, or Marked then Marked, not taking care of children.
    Is it more understandable?

    For the sample of Queries, thanks, our app is developed with UITest in mind, so elements have IDs from the Xamarin Forms code, in order to be shared on all platforms.

  • RichardSloggettRichardSloggett GBUniversity ✭✭

    Just in case anyone else stumbles across this post in an effort to target specific instances of common child controls, I think I have found the solution.

    Use app.Query(c => c.Marked("UserInfoCell").Descendant().Marked("Button"))

    The .Descendant() method switches the context of the subsequent calls to look at the descendants - so the second .Marked("Button") queries the descendants of the first .Marked("UserInfoCall") call rather than just querying the elements that are the direct result.

    It is a shame there isn't an example of this in the Xamarin UI Test docs. Even moderately complex UIs will have re-usable elements and therefore "homing in" on a particular instance of a view in this manner is a pretty common requirements in coded UI I think.

Sign In or Register to comment.