react-nativedetox

detox tests not recognising View even after adding id


Apologies in advance if this has been asked before. I came across the detox e2e framework for react native apps, and I thought of giving it a try.

I am trying to automate this demo mobile application given here - link Since the tests in detox uses testID as one of the locators so I added one in the LoginScreenMaterial.js file inside app/screen/LoginScreenMaterial.js like this

<View testID="login_screen" style={{width: this._width, justifyContent: 'center'}}>
          <RkCard style={styles.container}>
            <View rkCardHeader style={styles.header}>
              <RkText/>
              <RkText style={styles.label}>Sign in into your account</RkText>
            </View>

However, even after sucessfully building the app, I ran the app with this simple test

it('should have welcome screen', async () => {
    await expect(element(by.id('login_screen'))).toBeVisible();
  });

However, the tests still fail with the element being failed to be recognised. What am I missing here in this tests? Can we not add testID like this explicitly in the .js file.

Edit 1 : Adding the error message

1) Example
       should have welcome screen:
     Error: Error: Cannot find UI Element.
Exception with Assertion: {
  "Assertion Criteria" : "assertWithMatcher:matcherForSufficientlyVisible(>=0.750000)",
  "Element Matcher" : "(((respondsToSelector(accessibilityIdentifier) && accessibilityID('login_screen')) && !(kindOfClass('RCTScrollView'))) || (kindOfClass('UIScrollView') && ((kindOfClass('UIView') || respondsToSelector(accessibilityContainer)) && ancestorThatMatches(((respondsToSelector(accessibilityIdentifier) && accessibilityID('login_screen')) && kindOfClass('RCTScrollView'))))))",
  "Recovery Suggestion" : "Check if the element exists in the UI hierarchy printed below. If it exists, adjust the matcher so that it accurately matches element."
}

Error Trace: [
  {
    "Description" : "Interaction cannot continue because the desired element was not found.",
    "Error Domain" : "com.google.earlgrey.ElementInteractionErrorDomain",
    "Error Code" : "0",
    "File Name" : "GREYElementInteraction.m",
    "Function Name" : "-[GREYElementInteraction matchedElementsWithTimeout:error:]",
    "Line" : "124"
  }
]
      at Client.execute (node_modules/detox/src/client/Client.js:74:13)

Solution

  • I took a look at the application and was able to get it to work. I set the following in my devDependencies.

      "devDependencies": {
        ...
        "jest": "23.2.0",
        "detox": "8.0.0"
        ...
      },
    

    To the package.json I also added

    "detox": {
        "configurations": {
          "ios.sim.debug": {
            "binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/BoomApp.app",
            "build": "xcodebuild -project ios/BoomApp.xcodeproj -scheme BoomApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
            "type": "ios.simulator",
            "name": "iPhone 7"
          }
        },
    

    I ran detox init -r jest

    I was then able to get it to recognise when a specific screen had been rendered I did this by adding the testID to the ScrollView LoginScreenBlur.js (line 23)

    <AppWrapper>
        <ScrollView contentContainerStyle={{flex: 1}} testID={'login_screen'}>
        ....
        </ScrollView>
    </AppWrapper>
    

    A then in e2e/firstTest.spec.js I replaced the tests with

      it('should have loginScreen', async () => {
        await expect(element(by.id('login_screen'))).toBeVisible();
      });
    

    This was my console response after running detox build && detox test

    node_modules/.bin/jest e2e --config=e2e/config.json --maxWorkers=1 --testNamePattern='^((?!:android:).)*$'
     server listening on localhost:64579...
     : Searching for device matching iPhone 7...
     : Uninstalling org.reactjs.native.example.BoomApp...
     : org.reactjs.native.example.BoomApp uninstalled
     : Installing /Users/work/Downloads/react-native-ui-kitten-demo-app-master/ios/build/Build/Products/Debug-iphonesimulator/BoomApp.app...
     : /Users/work/Downloads/react-native-ui-kitten-demo-app-master/ios/build/Build/Products/Debug-iphonesimulator/BoomApp.app installed
     : Terminating org.reactjs.native.example.BoomApp...
     : org.reactjs.native.example.BoomApp terminated
     : Launching org.reactjs.native.example.BoomApp...
    7: org.reactjs.native.example.BoomApp launched. The stdout and stderr logs were recreated, you can watch them with:
            tail -F /Users/work/Library/Developer/CoreSimulator/Devices/AF406169-5CF3-4480-9D00-8F934C420043/data/tmp/detox.last_launch_app_log.{out,err}
     PASS  e2e/firstTest.spec.js (7.935s)
      Example
        ✓ should have loginScreen (1499ms)
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        8.87s, estimated 9s
    Ran all test suites matching /e2e/i with tests matching "^((?!:android:).)*$".
    

    It would seem that the app defaults to launching LoginScreenBlur, so it would make sense to test it first, rather than LoginScreenMaterial.

    One thing I have noticed is that the application uses RKTextInput and RkButton, these are not native components but wrappers around native components. This means that you will need to pass the testID down to the native component that you want to have the testID. I am not sure if react-native-ui-kitten supports accessibility labels, so there may be some more work ahead if you wish to automate input of text and button taps.

    Adding testID to custom components

    See Step 3 https://github.com/wix/detox/blob/master/docs/Introduction.WritingFirstTest.md

    Note that not all React components support this prop. Most of the built-in native components in React Native like View, Text, TextInput, Switch, ScrollView have support though. If you create your own composite components, you will have to propagate this prop manually to the correct native component.

    A more detailed explanation of adding testID to custom components is given here https://github.com/wix/detox/blob/master/docs/Troubleshooting.RunningTests.md#cant-find-my-component-even-though-i-added-testid-to-its-props

    Briefly you should implement your custom component as follows.

    Custom Component

    export class MyCompositeComponent extends Component {
      render() {
        return (
          <TouchableOpacity testID={this.props.testID}>
            <View>
              <Text>Something something</Text>
            </View>
          </TouchableOpacity>
        );
      }
    }
    

    Using Custom Component

    Then you should use it like this.

    render() {
      return <MyCompositeComponent testID='MyUniqueId123' />;
    }
    

    Searching the hierarchy

    If you have done the above and you are sure your item has the correct testID and the tests are still failing, then you can search for it in the view hierarchy https://github.com/wix/Detox/blob/master/docs/troubleshooting/running-tests.md#debug-view-hierarchy

    I won't repeat the above post in full but the steps are

    1. Start a debuggable app (not a release build) in your simulator
    2. Open Xcode
    3. Attach Xcode to your app's process
    4. Press the Debug View Hierarchy button
    5. This will open the hierarchy viewer, and will show a breakdown of your app's native view hierarchy. Here you can browse through the views
    6. React Native testIDs are manifested as accessibility identifiers in the native view hierarchy