Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)
like image 440
demouser123 Avatar asked Jun 29 '18 05:06

demouser123


People also ask

What is detox in testing?

Detox is a JavaScript mobile testing framework that is built into the application so that the test execution starts with the app launch. This makes test execution fast and robust because no additional external tools are needed to orchestrate and synchronize tests.


1 Answers

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.RunningTests.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
like image 182
Andrew Avatar answered Sep 28 '22 09:09

Andrew