mirror of
https://github.com/zhigang1992/react-native-firebase.git
synced 2026-04-23 20:10:05 +08:00
[tests] Move test suite into same repo
This commit is contained in:
51
tests/src/components/Banner.js
Normal file
51
tests/src/components/Banner.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, View, Text } from 'react-native';
|
||||
|
||||
function Banner({ type, children, style, textStyle }) {
|
||||
return (
|
||||
<View style={[styles.banner, styles[type || 'default'], style]}>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={[styles.bannerText, textStyle]}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
Banner.propTypes = {
|
||||
type: React.PropTypes.oneOf([
|
||||
'success',
|
||||
'warning',
|
||||
'error',
|
||||
'info',
|
||||
]),
|
||||
children: React.PropTypes.oneOfType([
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.array,
|
||||
]).isRequired,
|
||||
style: View.propTypes.style,
|
||||
textStyle: Text.propTypes.style,
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
banner: {
|
||||
alignItems: 'center',
|
||||
elevation: 3,
|
||||
},
|
||||
bannerText: {
|
||||
color: '#ffffff',
|
||||
},
|
||||
warning: {
|
||||
backgroundColor: '#FFC107',
|
||||
},
|
||||
error: {
|
||||
backgroundColor: '#f44336',
|
||||
},
|
||||
success: {
|
||||
backgroundColor: '#4CAF50',
|
||||
},
|
||||
});
|
||||
|
||||
export default Banner;
|
||||
75
tests/src/components/Icon.js
Normal file
75
tests/src/components/Icon.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import React from 'react';
|
||||
import { View, TouchableHighlight } from 'react-native';
|
||||
import VectorIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
type Props = {
|
||||
name: string,
|
||||
size?: number,
|
||||
color?: string,
|
||||
allowFontScaling?: boolean,
|
||||
style?: Object,
|
||||
rotate?: number,
|
||||
onPress?: () => void,
|
||||
underlayColor?: string,
|
||||
};
|
||||
|
||||
// TODO Spin?
|
||||
class Icon extends React.Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.measured = false;
|
||||
this.state = {
|
||||
width: 0,
|
||||
};
|
||||
}
|
||||
|
||||
setDimensions(e) {
|
||||
if (!this.measured) {
|
||||
this.measured = true;
|
||||
this.setState({
|
||||
width: e.nativeEvent.layout.width,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
props: Props;
|
||||
|
||||
render() {
|
||||
const { name, size = 24, color = '#757575', allowFontScaling = true, style, rotate, onPress, underlayColor } = this.props;
|
||||
|
||||
const icon = (
|
||||
<View
|
||||
onLayout={(e) => this.setDimensions(e)}
|
||||
style={[
|
||||
style,
|
||||
rotate ? { transform: [{ rotate: `${rotate}deg` }] } : null,
|
||||
]}
|
||||
>
|
||||
<VectorIcon
|
||||
name={name.toLowerCase().replace(/\s+/g, '-')}
|
||||
size={size}
|
||||
color={color}
|
||||
allowFontScaling={allowFontScaling}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
if (!onPress) {
|
||||
return icon;
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableHighlight
|
||||
underlayColor={underlayColor || 'rgba(0, 0, 0, 0.054)'}
|
||||
onPress={onPress}
|
||||
style={{ padding: 8, borderRadius: (this.state.width + 8) / 2 }}
|
||||
>
|
||||
{icon}
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Icon;
|
||||
70
tests/src/components/OverviewControlButton.js
Normal file
70
tests/src/components/OverviewControlButton.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import React, { PropTypes, Component } from 'react';
|
||||
import some from 'lodash.some';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Toast from 'react-native-simple-toast';
|
||||
|
||||
import { runTests } from '../tests/index';
|
||||
import RunStatus from '../../lib/RunStatus';
|
||||
|
||||
import Icon from '../components/Icon';
|
||||
|
||||
class OverviewControlButton extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.handleOnPress = this.handleOnPress.bind(this);
|
||||
}
|
||||
|
||||
testSuitesAreRunning() {
|
||||
const { testSuites } = this.props;
|
||||
|
||||
return some(Object.values(testSuites), ({ status }) => status === RunStatus.RUNNING);
|
||||
}
|
||||
|
||||
handleOnPress() {
|
||||
const { focusedTestIds, pendingTestIds, tests } = this.props;
|
||||
runTests(tests, { focusedTestIds, pendingTestIds });
|
||||
Toast.show('Running all suite tests.');
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.testSuitesAreRunning()) {
|
||||
return (
|
||||
<Icon
|
||||
color={'#ffffff'}
|
||||
size={28}
|
||||
name="autorenew"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Icon
|
||||
color={'#ffffff'}
|
||||
size={28}
|
||||
name="play circle filled"
|
||||
onPress={this.handleOnPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
OverviewControlButton.propTypes = {
|
||||
tests: PropTypes.objectOf(PropTypes.object).isRequired,
|
||||
testSuites: PropTypes.objectOf(PropTypes.object).isRequired,
|
||||
focusedTestIds: PropTypes.objectOf(PropTypes.bool).isRequired,
|
||||
pendingTestIds: PropTypes.objectOf(PropTypes.bool).isRequired,
|
||||
};
|
||||
|
||||
|
||||
function mapStateToProps({ tests, testSuites, focusedTestIds, pendingTestIds }) {
|
||||
return {
|
||||
tests,
|
||||
testSuites,
|
||||
focusedTestIds,
|
||||
pendingTestIds,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(OverviewControlButton);
|
||||
52
tests/src/components/StatusIndicator.js
Normal file
52
tests/src/components/StatusIndicator.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import { View, Text } from 'react-native';
|
||||
import React, { PropTypes, Component } from 'react';
|
||||
|
||||
import RunStatus from '../../lib/RunStatus';
|
||||
import Icon from './Icon';
|
||||
|
||||
class StatusIndicator extends Component {
|
||||
|
||||
render() {
|
||||
const { status, progress } = this.props;
|
||||
|
||||
switch (status) {
|
||||
case RunStatus.RUNNING:
|
||||
if (progress > 0) {
|
||||
return (
|
||||
<View style={{ width: 30, flex: 1, justifyContent: 'flex-end' }}>
|
||||
<Text style={{ fontSize: 11, marginBottom: 20 }}>
|
||||
{progress.toFixed(0)}%
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Icon color={'rgba(0, 0, 0, 0.2)'} name="autorenew" />
|
||||
);
|
||||
case RunStatus.OK:
|
||||
return (
|
||||
<Icon name={'done'} />
|
||||
);
|
||||
case RunStatus.ERR:
|
||||
return (
|
||||
<Icon color={'#f44336'} name="clear" />
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StatusIndicator.propTypes = {
|
||||
status: PropTypes.oneOf(Object.values(RunStatus)),
|
||||
progress: PropTypes.number,
|
||||
};
|
||||
|
||||
StatusIndicator.defaultProps = {
|
||||
status: null,
|
||||
progress: 0
|
||||
};
|
||||
|
||||
module.exports = StatusIndicator;
|
||||
71
tests/src/components/TestControlButton.js
Normal file
71
tests/src/components/TestControlButton.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import React, { PropTypes, Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import Toast from 'react-native-simple-toast';
|
||||
|
||||
import RunStatus from '../../lib/RunStatus';
|
||||
import { runTest } from '../tests/index';
|
||||
|
||||
import Icon from './Icon';
|
||||
|
||||
class TestControlButton extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.handleOnPress = this.handleOnPress.bind(this);
|
||||
}
|
||||
|
||||
testIsPending() {
|
||||
const { test: { id }, pendingTestIds } = this.props;
|
||||
return !!pendingTestIds[id];
|
||||
}
|
||||
|
||||
handleOnPress() {
|
||||
const { test: { id, description } } = this.props;
|
||||
|
||||
runTest(id);
|
||||
Toast.show(`Running ${description}.`);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { test: { status } } = this.props;
|
||||
|
||||
if (status !== RunStatus.STARTED && !this.testIsPending()) {
|
||||
return (
|
||||
<Icon
|
||||
color={'#ffffff'}
|
||||
size={28}
|
||||
name="play circle filled"
|
||||
onPress={this.handleOnPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TestControlButton.propTypes = {
|
||||
test: PropTypes.shape({
|
||||
id: PropTypes.number.isRequired,
|
||||
status: PropTypes.string,
|
||||
description: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
|
||||
pendingTestIds: PropTypes.objectOf(PropTypes.bool).isRequired,
|
||||
};
|
||||
|
||||
TestControlButton.defaultProps = {
|
||||
|
||||
};
|
||||
|
||||
function mapStateToProps({ tests, pendingTestIds }, { testId }) {
|
||||
const test = tests[testId];
|
||||
|
||||
return {
|
||||
test,
|
||||
pendingTestIds,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps)(TestControlButton);
|
||||
96
tests/src/components/TestSuiteControlButton.js
Normal file
96
tests/src/components/TestSuiteControlButton.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import React, { PropTypes, Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Toast from 'react-native-simple-toast';
|
||||
|
||||
import RunStatus from '../../lib/RunStatus';
|
||||
import { runTests } from '../tests/index';
|
||||
|
||||
import Icon from './Icon';
|
||||
|
||||
class TestSuiteControlButton extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.toggleOnlyShowFailingTests = this.toggleOnlyShowFailingTests.bind(this);
|
||||
this.startTestSuite = this.startTestSuite.bind(this);
|
||||
}
|
||||
|
||||
startTestSuite() {
|
||||
const { testSuite: { name, testIds }, tests, focusedTestIds, pendingTestIds } = this.props;
|
||||
|
||||
const testSuiteTests = testIds.reduce((memo, testId) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
memo[testId] = tests[testId];
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
runTests(testSuiteTests, { focusedTestIds, pendingTestIds });
|
||||
|
||||
Toast.show(`Running ${name} tests.`);
|
||||
}
|
||||
|
||||
toggleOnlyShowFailingTests() {
|
||||
const { onlyShowFailingTests, onFilterChange } = this.props;
|
||||
onFilterChange({ onlyShowFailingTests: !onlyShowFailingTests });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { testSuite: { status }, onlyShowFailingTests } = this.props;
|
||||
|
||||
if (status === RunStatus.ERR) {
|
||||
return (
|
||||
<Icon
|
||||
color={onlyShowFailingTests ? '#ffffff' : 'rgba(255, 255, 255, 0.54)'}
|
||||
size={28}
|
||||
name="error outline"
|
||||
onPress={this.toggleOnlyShowFailingTests}
|
||||
/>
|
||||
);
|
||||
} else if (status !== RunStatus.RUNNING) {
|
||||
return (
|
||||
<Icon
|
||||
color={'#ffffff'}
|
||||
size={28}
|
||||
name="play circle filled"
|
||||
onPress={this.startTestSuite}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TestSuiteControlButton.propTypes = {
|
||||
testSuite: PropTypes.shape({
|
||||
status: PropTypes.oneOf(Object.values(RunStatus)),
|
||||
}).isRequired,
|
||||
|
||||
tests: PropTypes.objectOf(PropTypes.object).isRequired,
|
||||
focusedTestIds: PropTypes.objectOf(PropTypes.bool).isRequired,
|
||||
pendingTestIds: PropTypes.objectOf(PropTypes.bool).isRequired,
|
||||
|
||||
onlyShowFailingTests: PropTypes.bool,
|
||||
|
||||
onFilterChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
TestSuiteControlButton.defaultProps = {
|
||||
onlyShowFailingTests: false,
|
||||
};
|
||||
|
||||
|
||||
function mapStateToProps({ tests, testSuites, focusedTestIds, pendingTestIds }, { testSuiteId }) {
|
||||
const testSuite = testSuites[testSuiteId];
|
||||
|
||||
return {
|
||||
tests,
|
||||
testSuite,
|
||||
focusedTestIds,
|
||||
pendingTestIds,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps)(TestSuiteControlButton);
|
||||
Reference in New Issue
Block a user