react-native-svg-charts
react-native-svg-charts copied to clipboard
[Feature] Support mocking
Any idea how we can mock this module and make better testing coverage?
I'm not sure if this issue is specific to mocking or unit testing in general but I had a really hard time getting unit tests around these chart components. For anyone who may end up here in the future, here's what I had to do to make my chart components testable. Using the following steps allowed me to reach 100% code coverage under Jest.
1. Trigger react-native-svg-charts
component onLayout
handlers
react-native-svg-charts
components listen for onLayout
changes before they render any data. You will need to trigger these event handlers for each component in your tests. This is a utility function I wrote to make that easy with @testing-library/react-native
.
function _findByProp(root: any, prop = '', found: any[] = []) {
if (!root) return found
if (root.props) {
if (Object.keys(root.props).includes(prop)) found.push(root)
}
if (root.children && root.children.length) {
root.children.forEach((c) => _findByProp(c, prop, found))
}
return found
}
/**
* Find components in the given component hierarchy based
* on the name of one of their props. For example,
* `findByProp(root, 'foo')` will return a list of all
* components with a `foo` prop of any value.
*/
export function findByProp(
/**
* The root of the component tree to search through.
*/
root: any,
/**
* The name of the prop you are using to select components.
*/
prop = '',
) {
return _findByProp(root, prop)
}
/**
* Fire the same layout event for all components that have
* an `onLayout` prop. Generally you won't want to do this because
* the layout dimensions would be different for each component.
* However, this can be useful if you don't have a way to determine
* which components should receive specific dimensions.
*/
export function fireLayoutEvent(
/**
* The root node to search for components with `onLayout` props.
*/
root: any,
/**
* The event options inside of `event.nativeElement.layout`
*/
options = {
width: 0,
height: 0,
},
) {
findByProp(root, 'onLayout').forEach((n) =>
fireEvent(n, 'layout', { nativeEvent: { layout: options } }),
)
}
Then in my test files:
beforeEach(() => {
const screen = render(component, options)
// Force a layout event.
fireLayoutEvent(screen.container, {width: 100, height: 100})
})
2. Mocks for react-native-svg
Add a mock implementation of react-native-svg
from here: https://gist.github.com/JCMais/8302a1646ccc9759237947a66bdda8e0
Adding these mocks makes it really simple to query the components, especially if you use @testing-library/react-native
3. Chart accessibility props
In order to query the chart elements, I was able to add accessibility attributes to the various chart components using the svg
prop they all have.
In my component code:
<XAxis
data={chartData}
svg={{
accessibilityRole: 'columnheader',
}}
></XAxis>
Then in my tests I was able to do the following:
// Get all x axis labels using the column header role
screen.getAllByRole('columnheader')
// Get an x axis label using its text
screen.getByText('January')
I was able to follow the accessibility paradigm described in the following article: https://tink.uk/accessible-svg-line-graphs/. This approach only works on web and test. For native I just treat my component as an image with a very descriptive label:
<Container
// On native, describe the chart as an image.
accessible={Platform.OS === 'web' ? undefined : true}
accessibilityLabel={Platform.OS === 'web' ? undefined : makeA11ySummary()}
accessibilityRole={Platform.OS === 'web' ? undefined : 'image'}
>
...
</Container>
I would love to know if there are better/easier approaches out there.