Gabriel Rohden
Gabriel Rohden

Reputation: 1356

How to match element by ancestor index in react-native detox

I'm trying to follow a personal pattern for react-native detox, which consist of something like this:


export const noteCardDescriptor = (baseID: string) => (index: number) => {
  return {
    get element() {
      return element(by.id(baseID)).atIndex(index);
    },
    get title() {
      return element(
        by.id('NoteCardTitle').withAncestor(by.id(baseID)),
      ).atIndex(index);
    },
    get date {
      return element(by.id('NoteCardDate').withAncestor(by.id(baseID))).atIndex(
        index,
      );
    },
  };
};

class MyScreen {
  static note = noteCardDescriptor('MyScreenNoteCard')
}

And using it in my tests like this:


const noteOne = MyScreen.note(0)

await expect(noteOne.title).toHaveText('Hello there')
await expect(noteOne.date).toNotBeVisible()

const noteTwo = MyScreen.note(1)
await expect(noteTwo.title).toHaveText('Hello there')
await expect(noteTwo.date).toHaveText(formatDate(date))

The problem with this approach is that when an element is optional (like date), the given index will fail to match properly because detox reads the matcher as:

instead of

so my question is:

Is there any way to tell detox to search first for a ancestor at IDX and only then find childrens for matches?

Something like this:

element(by.id(X).withAncestor(by.id(Y).atIndex(0))

Note: I know I can transform the optional field getter into a method and override the index, but reading it is confusing since I've already specified the ''ancestor'' index previously

Upvotes: 0

Views: 1602

Answers (1)

joachimwedin
joachimwedin

Reputation: 1564

Detox matchers does not quite work in the way that you are trying to use them. As stated in the docs:

Detox uses matchers to match UI elements in your app.

They are not intended for tree traversals to extract elements. A matcher gives you a set of elements that matches a particular criteria. The atIndex matcher allows you specify which element to chose if a matcher returns multiple UI elements. So, your example

element(by.id(X).withAncestor(by.id(Y).atIndex(0))

should rather be read as: find all UI elements elements with id X, then only keep the elements that have an ancestor with id Y, and of these remaining elements, chose the one with index 0.

The documentation highlights some problems with the atIndex matcher, so I would advice against using it:

Due to different underlying implementations of Detox on iOS and Android, as well as other differences in the OS implementations, as well as RN implementation differences on each OS, indices may not match between iOS and Android. Relying on indices may also introduce flakiness in your tests as your app's user interface is updated. It is recommended to use unique identifier matchers for your elements.

Upvotes: 2

Related Questions