[tests] improved testing framework error/success output

This commit is contained in:
Salakar 2017-07-18 05:59:28 +01:00
parent d8fee7f5cb
commit c4d066fd55
8 changed files with 140 additions and 49 deletions

View File

@ -6,6 +6,21 @@ const EVENTS = {
TEST_STATUS: 'TEST_STATUS',
};
const locationRegex = /\(?http:.*:([0-9]+):([0-9]+)\)?/g;
function cleanStack(stack, maxLines = 5) {
const lines = stack.split('\n').slice(0, maxLines + 1);
const out = [];
for (let i = 0, len = lines.length; i < len; i++) {
const srcLine = lines[i].trim();
out.push(srcLine.replace(locationRegex, '()'));
}
return out.join('\r\n');
}
/**
* Class that encapsulates synchronously running a suite's tests.
*/
@ -212,6 +227,14 @@ class TestRun {
}).then(() => true).catch(() => false);
}
/**
*
* @param testContext
* @param error
* @param testStart
* @param errorPrefix
* @private
*/
_reportAllTestsAsFailed(testContext, error, testStart, errorPrefix) {
testContext.tests.forEach((test) => {
this._reportTestError(test, error, Date.now() - testStart, errorPrefix);
@ -253,6 +276,13 @@ class TestRun {
if (error) {
this._reportTestError(test, error, Date.now() - testStart);
console.groupCollapsed(`%c ❌ Test Failed: ${test.description} (${this.testSuite.name})`, 'color: #f44336;');
console.log(`Test Description: ${test.description}`);
console.log(`Test Time Taken: ${Date.now() - testStart}`);
console.log(`Suite Name: ${this.testSuite.name}`);
console.log(`Suite Description: ${this.testSuite.description}`);
console.log(error);
console.groupEnd();
} else {
// eslint-disable-next-line no-param-reassign
test.status = RunStatus.OK;
@ -263,6 +293,13 @@ class TestRun {
time: Date.now() - testStart,
message: '',
});
console.groupCollapsed(`%c ✅ Test Passed: ${test.description} (${this.testSuite.name})`, 'color: #4CAF50;');
console.log(`Test Description: ${test.description}`);
console.log(`Test Time Taken: ${Date.now() - testStart}`);
console.log(`Suite Name: ${this.testSuite.name}`);
console.log(`Suite Description: ${this.testSuite.description}`);
console.groupEnd();
}
// Update suite progress
@ -279,31 +316,44 @@ class TestRun {
await this._runHookChain(test, testStart, testContext, 'afterEach', afterEachHooks);
}
})
.catch((error) => {
}).catch((error) => {
this._updateStatus(EVENTS.TEST_SUITE_STATUS, {
suiteId: this.testSuite.id,
status: RunStatus.ERR,
time: Date.now() - this.runStartTime,
message: `Test suite failed: ${error.message}`
message: `Test suite failed: ${error.message}`,
});
});
}
/**
*
* @param test
* @param error
* @param time
* @param errorPrefix
* @private
*/
_reportTestError(test, error, time, errorPrefix = '') {
// eslint-disable-next-line no-param-reassign
test.status = RunStatus.ERR;
this._updateStatus(EVENTS.TEST_STATUS, {
testId: test.id,
status: RunStatus.ERR,
time,
message: `${errorPrefix}${error.message ? `${error.name}: ${error.message}` : error}`,
stackTrace: error.stack,
stackTrace: cleanStack(error.stack),
});
}
/**
*
* @param func
* @param timeOutDuration
* @param description
* @return {Promise.<*>}
* @private
*/
async _safelyRunFunction(func, timeOutDuration, description) {
const syncResultOrPromise = captureThrownErrors(func);

View File

@ -3,7 +3,7 @@
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"start": "node node_modules/react-native/local-cli/cli.js start --platforms ios,android",
"android:dev": "react-native run-android",
"android:prod": "react-native run-android --configuration=release",
"ios:dev": "react-native run-ios",

View File

@ -39,7 +39,7 @@ const styles = StyleSheet.create({
color: '#ffffff',
},
warning: {
backgroundColor: '#FFC107',
backgroundColor: '#f57c00',
},
error: {
backgroundColor: '#f44336',

View File

@ -22,7 +22,7 @@ class CoreContainer extends React.Component {
*/
componentDidMount() {
if (Platform.OS === 'android') {
StatusBar.setBackgroundColor('#0279ba');
StatusBar.setBackgroundColor('#1976D2');
}
if (Platform.OS === 'ios') {
StatusBar.setBarStyle('light-content');

View File

@ -1,11 +1,10 @@
import * as testActions from '../actions/TestActions';
import { flatten, unflatten } from 'deeps';
import * as testActions from '../actions/TestActions';
import { initialState } from '../tests/index';
const initState = initialState();
function testsReducers(state = initState.tests, action: Object): State {
if (action.type === testActions.TEST_SET_STATUS) {
const flattened = flatten(state);

View File

@ -14,10 +14,12 @@ class Overview extends React.Component {
static navigationOptions = {
title: 'Test Suites',
headerTintColor: '#ffffff',
headerStyle: { backgroundColor: '#0288d1' },
headerRight: <View style={{ marginRight: 8 }}>
<OverviewControlButton />
</View>
headerStyle: { backgroundColor: '#1976D2' },
headerRight: (
<View style={{ marginRight: 8 }}>
<OverviewControlButton />
</View>
),
};
/**

View File

@ -15,14 +15,16 @@ class Suite extends React.Component {
return {
title,
headerTintColor: '#ffffff',
headerStyle: { backgroundColor: '#0288d1' },
headerRight: <View style={{ flexDirection: 'row', marginRight: 8 }}>
<TestSuiteControlButton
testSuiteId={testSuiteId}
onlyShowFailingTests={onlyShowFailingTests}
onFilterChange={setParams}
/>
</View>,
headerStyle: { backgroundColor: '#1976D2' },
headerRight: (
<View style={{ flexDirection: 'row', marginRight: 8 }}>
<TestSuiteControlButton
testSuiteId={testSuiteId}
onlyShowFailingTests={onlyShowFailingTests}
onFilterChange={setParams}
/>
</View>
),
};
};

View File

@ -13,10 +13,12 @@ class Test extends React.Component {
return {
title,
headerTintColor: '#ffffff',
headerStyle: { backgroundColor: '#0288d1' },
headerRight: <View style={{ marginRight: 8 }}>
<TestControlButton testId={testId} />
</View>,
headerStyle: { backgroundColor: '#1976D2' },
headerRight: (
<View style={{ marginRight: 8 }}>
<TestControlButton testId={testId} />
</View>
),
};
};
@ -52,31 +54,38 @@ class Test extends React.Component {
}
render() {
const { test: { stackTrace, description, func, status, time }, testContextName } = this.props;
const { test: { stackTrace, message, description, func, status, time }, testContextName } = this.props;
return (
<View style={styles.container}>
{Test.renderBanner({ status, time })}
<View >
<ScrollView style={styles.sectionContainer}>
<ScrollView >
<View style={styles.sectionContainer}>
<Text style={styles.heading}>{testContextName}</Text>
<Text style={styles.description}>{description}</Text>
</ScrollView>
<ScrollView style={styles.sectionContainer}>
<Text style={styles.heading}>Test Error</Text>
</View>
{message ? <View style={styles.sectionContainer}>
<Text style={styles.headingWarn}>Test Error Message</Text>
<Text style={styles.message}>{message || 'None.'}</Text>
</View> : null }
{stackTrace ? <View style={styles.sectionContainer}>
<Text style={styles.headingWarn}>Test Error Stack</Text>
<Text style={styles.description}>
<Text>{stackTrace || 'None.'}</Text>
{stackTrace || 'None.'}
</Text>
</View> : null }
<View style={styles.sectionContainer}>
<Text style={styles.heading}>
Test Code Preview
</Text>
</ScrollView>
<Text style={styles.heading}>
Test Code Preview
</Text>
<ScrollView style={styles.sectionContainer}>
<Text style={styles.description}>
{beautify(removeLastLine(removeFirstLine(func.toString())), { indent_size: 4, break_chained_methods: true })}
{beautify(removeLastLine(removeFirstLine(func.toString())), {
indent_size: 4,
break_chained_methods: true,
})}
</Text>
</ScrollView>
</View>
</View>
</ScrollView>
</View>
);
}
@ -87,7 +96,8 @@ Test.propTypes = {
status: PropTypes.string,
time: PropTypes.number,
func: PropTypes.function,
stackTrace: PropTypes.function,
stackTrace: PropTypes.string,
message: PropTypes.string,
description: PropTypes.string,
}).isRequired,
@ -101,29 +111,57 @@ Test.propTypes = {
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ffffff',
backgroundColor: '#fff',
},
sectionContainer: {
minHeight: 100,
backgroundColor: '#fff',
},
heading: {
padding: 5,
backgroundColor: '#0288d1',
fontWeight: '600',
elevation: 3,
backgroundColor: '#2196F3',
fontWeight: '400',
color: '#ffffff',
fontSize: 16,
fontSize: 14,
},
headingWarn: {
padding: 5,
elevation: 3,
backgroundColor: '#FFC107',
fontWeight: '400',
color: '#212121',
fontSize: 14,
},
description: {
padding: 5,
fontSize: 14,
fontSize: 12,
},
message: {
padding: 5,
fontSize: 12,
width: '100%',
minHeight: 100,
},
});
/*
.dark-primary-color { background: #1976D2; }
.default-primary-color { background: #2196F3; }
.light-primary-color { background: #BBDEFB; }
.text-primary-color { color: #FFFFFF; }
.accent-color { background: #FFC107; }
.primary-text-color { color: #212121; }
.secondary-text-color { color: #757575; }
.divider-color { border-color: #BDBDBD; }
*/
function select({ tests, testContexts }, { navigation: { state: { params: { testId } } } }) {
const test = tests[testId];
let testContext = testContexts[test.testContextId];
while(testContext.parentContextId && testContexts[testContext.parentContextId].parentContextId) {
while (testContext.parentContextId && testContexts[testContext.parentContextId].parentContextId) {
testContext = testContexts[testContext.parentContextId];
}
return {