259 lines
6.6 KiB
Markdown
259 lines
6.6 KiB
Markdown
---
|
|
id: tests
|
|
title: Testing
|
|
sidebar_label: Testing
|
|
---
|
|
## Jest
|
|
|
|
To test a component which use RNCamera, you need to create a react-native-camera.js file inside your **mocks** folder on the root of your project with the following content:
|
|
|
|
```javascript
|
|
import React from 'react';
|
|
|
|
const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
|
|
export class RNCamera extends React.Component {
|
|
static Constants = {
|
|
Aspect: {},
|
|
BarCodeType: {},
|
|
Type: { back: 'back', front: 'front' },
|
|
CaptureMode: {},
|
|
CaptureTarget: {},
|
|
CaptureQuality: {},
|
|
Orientation: {},
|
|
FlashMode: {},
|
|
TorchMode: {},
|
|
};
|
|
|
|
takePictureAsync = async () => {
|
|
await timeout(2000);
|
|
return {
|
|
base64: 'base64string',
|
|
};
|
|
};
|
|
|
|
render() {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export default RNCamera;
|
|
```
|
|
|
|
You don't need to do anything else in your test, because Jest will use the mock in your test instead of the native module.
|
|
|
|
### Example
|
|
|
|
We are going to create a component which uses RNCamera which two simple features:
|
|
|
|
- Take a photo
|
|
- Change camera between front or back
|
|
|
|
The custom component PhotoCamera is the following:
|
|
|
|
```javascript
|
|
import React from 'react';
|
|
import { View, TouchableOpacity, StyleSheet, Dimensions } from 'react-native';
|
|
import { RNCamera } from 'react-native-camera';
|
|
import Icon from 'react-native-vector-icons/FontAwesome';
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
backgroundColor: 'black',
|
|
},
|
|
preview: {
|
|
flex: 1,
|
|
justifyContent: 'flex-end',
|
|
alignItems: 'center',
|
|
},
|
|
topButtons: {
|
|
flex: 1,
|
|
width: Dimensions.get('window').width,
|
|
alignItems: 'flex-start',
|
|
},
|
|
bottomButtons: {
|
|
flex: 1,
|
|
width: Dimensions.get('window').width,
|
|
justifyContent: 'flex-end',
|
|
alignItems: 'center',
|
|
},
|
|
|
|
flipButton: {
|
|
flex: 1,
|
|
marginTop: 20,
|
|
right: 20,
|
|
alignSelf: 'flex-end',
|
|
},
|
|
recordingButton: {
|
|
marginBottom: 10,
|
|
},
|
|
});
|
|
|
|
class PhotoCamera extends React.PureComponent {
|
|
state = {
|
|
type: RNCamera.Constants.Type.back,
|
|
};
|
|
|
|
flipCamera = () =>
|
|
this.setState({
|
|
type:
|
|
this.state.type === RNCamera.Constants.Type.back
|
|
? RNCamera.Constants.Type.front
|
|
: RNCamera.Constants.Type.back,
|
|
});
|
|
|
|
takePhoto = async () => {
|
|
const { onTakePhoto } = this.props;
|
|
const options = {
|
|
quality: 0.5,
|
|
base64: true,
|
|
width: 300,
|
|
height: 300,
|
|
};
|
|
const data = await this.camera.takePictureAsync(options);
|
|
onTakePhoto(data.base64);
|
|
};
|
|
render() {
|
|
const { type } = this.state;
|
|
return (
|
|
<View style={styles.container}>
|
|
<RNCamera
|
|
ref={cam => {
|
|
this.camera = cam;
|
|
}}
|
|
type={type}
|
|
style={styles.preview}
|
|
/>
|
|
<View style={styles.topButtons}>
|
|
<TouchableOpacity onPress={this.flipCamera} style={styles.flipButton}>
|
|
<Icon name="refresh" size={35} color="orange" />
|
|
</TouchableOpacity>
|
|
</View>
|
|
<View style={styles.bottomButtons}>
|
|
<TouchableOpacity onPress={this.takePhoto} style={styles.recordingButton}>
|
|
<Icon name="camera" size={50} color="orange" />
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default PhotoCamera;
|
|
```
|
|
|
|
And here is our test to check if it renders properly:
|
|
|
|
```javascript
|
|
import React from 'react';
|
|
import Adapter from 'enzyme-adapter-react-16';
|
|
import { shallow, configure } from 'enzyme';
|
|
import MyPhotoCamera from './';
|
|
|
|
configure({ adapter: new Adapter() });
|
|
|
|
describe('PhotoCamera Tests', () => {
|
|
test('renders correctly', () => {
|
|
const wrapper = shallow(<MyPhotoCamera />);
|
|
expect(wrapper).toMatchSnapshot();
|
|
});
|
|
});
|
|
```
|
|
|
|
Also, here is the complete test of the whole component with 100% coverage:
|
|
|
|
```javascript
|
|
import React from 'react';
|
|
import { TouchableOpacity } from 'react-native';
|
|
import Adapter from 'enzyme-adapter-react-16';
|
|
import { shallow, configure, mount } from 'enzyme';
|
|
import PhotoCamera from '../';
|
|
|
|
const { JSDOM } = require('jsdom');
|
|
|
|
const jsdom = new JSDOM();
|
|
const { window } = jsdom;
|
|
|
|
function copyProps(src, target) {
|
|
const props = Object.getOwnPropertyNames(src)
|
|
.filter(prop => typeof target[prop] === 'undefined')
|
|
.map(prop => Object.getOwnPropertyDescriptor(src, prop));
|
|
Object.defineProperties(target, props);
|
|
}
|
|
|
|
global.window = window;
|
|
global.document = window.document;
|
|
global.navigator = {
|
|
userAgent: 'node.js',
|
|
};
|
|
copyProps(window, global);
|
|
|
|
// Ignore React Web errors when using React Native
|
|
// but still show relevant errors
|
|
const suppressedErrors = /(React does not recognize the.*prop on a DOM element|Unknown event handler property|is using uppercase HTML|Received `true` for a non-boolean attribute `accessible`|The tag.*is unrecognized in this browser)|is using incorrect casing|Received `true` for a non-boolean attribute `enabled`/;
|
|
const realConsoleError = console.error; // eslint-disable-line
|
|
// eslint-disable-next-line
|
|
console.error = message => {
|
|
if (message.match(suppressedErrors)) {
|
|
return;
|
|
}
|
|
realConsoleError(message);
|
|
};
|
|
|
|
configure({ adapter: new Adapter() });
|
|
|
|
describe('PhotoCamera Tests', () => {
|
|
test('renders correctly', () => {
|
|
const wrapper = shallow(<PhotoCamera />);
|
|
expect(wrapper).toMatchSnapshot();
|
|
});
|
|
test('initial state should be back camera', () => {
|
|
const wrapper = shallow(<PhotoCamera />);
|
|
expect(wrapper.state().type).toBe('back');
|
|
});
|
|
test('should flip the camera from back to front', () => {
|
|
const wrapper = shallow(<PhotoCamera />);
|
|
expect(wrapper.state().type).toBe('back');
|
|
wrapper
|
|
.find(TouchableOpacity)
|
|
.first()
|
|
.props()
|
|
.onPress();
|
|
expect(wrapper.state().type).toBe('front');
|
|
});
|
|
|
|
test('should flip the camera from front to back if touch flip button and curren state is ', () => {
|
|
const wrapper = shallow(<PhotoCamera />);
|
|
wrapper.setState({
|
|
type: 'front',
|
|
});
|
|
wrapper.update();
|
|
wrapper
|
|
.find(TouchableOpacity)
|
|
.first()
|
|
.props()
|
|
.onPress();
|
|
expect(wrapper.state().type).toBe('back');
|
|
});
|
|
|
|
test('should have a reference to the React Native Camera module', () => {
|
|
const wrapper = mount(<PhotoCamera />);
|
|
expect(wrapper.instance().camera).toBeDefined();
|
|
});
|
|
|
|
test('test onPress functionality', async () => {
|
|
const onTakePhotoEvent = jest.fn(data => data);
|
|
const wrapper = mount(<PhotoCamera onTakePhoto={onTakePhotoEvent} />);
|
|
await wrapper
|
|
.find(TouchableOpacity)
|
|
.at(1)
|
|
.props()
|
|
.onPress();
|
|
expect(onTakePhotoEvent.mock.calls.length).toBe(1);
|
|
});
|
|
});
|
|
```
|