cavy: `findComponent` doesn't work as expected on components.

According to the README.md, <Text> components should be inspectable using findComponent, and I’ve been unable to do this. In the following example spec:

export default (spec) => {
  spec.describe('Test', () => {
    spec.it('works', async () => {
      await spec.fillIn('Test.Input', 'test');
      const text = await spec.findComponent('Test.Text');
      await containsText(text, 'test');
    })
  })
}

Assume here that Test.Input and Test.Text exist, that filling in Test.Input sets the text in Test.Text, and that containsText matches the example in the README.md. When this test is run, it returns an error, logging Cannot read property 'children' of undefined. It looks like findComponent isn’t returning what is expected.

This is on React Native 0.56.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 15 (7 by maintainers)

Most upvoted comments

Hi everyone! A quick update on the above issue.

The problem For components like Text, React Native renderer creates an instance of ReactNativeFiberHostComponent which is what gets passed to the ref callback. You cannot access the original props on this so testing anything other than the fact the element is on the page is impossible.

The solution (update to wrap function) At the moment Cavy’s wrap function only works with function components, forwarding the ref so that it can call the inner component’s props.

We’re proposing that you should be able to pass a non-function component like Text to wrap. In this case, a React class component with testable props is returned.

You’ll then be able to test Text as follows:

// src/MyPage.js

import React from 'react';
import { View, Text } from 'react-native';
import { useCavy, wrap } from 'cavy';

export default ({ data }) => {
  const generateTestHook = useCavy();
  const TestableText = wrap(Text);

  return (
    <View>
      <TestableText ref={generateTestHook('MyPage.Title')}>
        {data.title}
      </TestableText>
    </View>
  )
};

In your test:

// specs/MySpec.js

import { containsText } from './helpers';

export default function(spec) {
  spec.describe('In this context', async function() {
    spec.it('shows this text', async function() {
      const textComponent = await spec.findComponent('MyPage.Title');
      await containsText(text, 'Welcome');
    });
  });
}

With custom helper function:

// specs/helpers.js

export async function containsText(component, text) {
  if (!component.props.children.includes(text)) {
    throw new Error(`Could not find text ${text}`);
  };
}

I also propose that we make that containsText helper ☝️ part of Cavy, as it seems it would be pretty popular (@jalada).

I’ll be working on this enhancement over the next day or so - will keep y’all updated.

Included in version 3.1.0 released today 🎉

No worries! I am quite busy at work, so I will probably do the PR with typings over the weekend anyway 😃 Thanks for addressing this issue!

Ahhh, that makes sense - thanks for sharing your solution @funador! Maybe something similar was at play when @sdroadie was having issues.

It might not be much extra help, but Cavy does have a helper function wrap that effectively allows you to add a ref to a function component. There’s an example of this in the ‘Hook up components’ section of the README.