No Description

reporter.js 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import React, {Component} from 'react';
  2. import {
  3. AppRegistry,
  4. StyleSheet,
  5. Text,
  6. View,
  7. Platform,
  8. ScrollView,
  9. ListView,
  10. Image,
  11. TouchableOpacity,
  12. Dimensions,
  13. RecyclerViewBackedScrollView,
  14. } from 'react-native';
  15. import AnimateNumber from '../animate-text.js'
  16. import Assert from './assert.js'
  17. import RNTEST from '../index.js'
  18. export default class Reporter extends Component {
  19. constructor(props:any) {
  20. super(props)
  21. this.tests = {
  22. common : []
  23. }
  24. this.state = {
  25. listHeight : 0
  26. }
  27. this.testGroups = ['summary','common']
  28. this.ds = null
  29. this.updateDataSource()
  30. }
  31. componentWillUpdate(nextProps, nextState) {
  32. this.updateDataSource()
  33. }
  34. render() {
  35. let tests = RNTEST.TestContext.getTests()
  36. let passed = 0
  37. let executed = 0
  38. let count = 0
  39. for(let i in tests) {
  40. if(tests[i].status !== 'skipped')
  41. count++
  42. if(tests[i].status !== 'waiting' && tests[i].status !== 'skipped')
  43. executed++
  44. passed += tests[i].status === 'pass' ? 1 : 0
  45. }
  46. let percent = passed / count
  47. let color = `rgb(${Math.floor((1-percent) *255)},${Math.floor(percent *192)}, 0)`
  48. return (
  49. <View style={{flex : 1}}>
  50. <View style={{margin : 20}} onLayout={(e) => {
  51. this.setState({
  52. headerHeight : e.nativeEvent.layout.height,
  53. listHeight : Dimensions.get('window').height - e.nativeEvent.layout.height
  54. })
  55. }}>
  56. <Text>{`${executed} tests executed`}</Text>
  57. <Text>{`${passed} test cases passed`}</Text>
  58. <Text>{`${count} test cases`}</Text>
  59. <View style={{flexDirection : 'row', alignSelf : 'center', alignItems : 'flex-end'}}>
  60. <AnimateNumber style={{
  61. color,
  62. fontSize : 100,
  63. textAlign : 'right'
  64. }}
  65. value={Math.floor(passed / count*100)}
  66. countBy={1}/>
  67. <Text style={{color, fontSize : 30, textAlign : 'left'}} >{`%`}</Text>
  68. </View>
  69. </View>
  70. <ListView
  71. style={[styles.container]}
  72. dataSource={this.ds}
  73. renderRow={this.renderTest.bind(this)}
  74. renderScrollComponent={props => <RecyclerViewBackedScrollView {...props} />}
  75. renderSectionHeader={(data, id) => {
  76. return (
  77. <View style={styles.sectionHeader}>
  78. <Text style={styles.sectionText}>{id}</Text>
  79. </View>
  80. )
  81. }}
  82. />
  83. </View>)
  84. }
  85. renderTest(t, group) {
  86. let pass = true
  87. let foundActions = false
  88. let tests = RNTEST.TestContext.getTests()
  89. if(Array.isArray(t.result) && !t.expired) {
  90. t.result = t.result.map((r) => {
  91. if(r.type.name === 'Assert' || r.type.name === 'Info') {
  92. foundActions = true
  93. let comp = r.props.comparer ? r.props.comparer(r.props.expect, r.props.actual) : (r.props.actual === r.props.expect)
  94. pass = pass && comp
  95. }
  96. return React.cloneElement(r, {desc : r.key})
  97. })
  98. }
  99. if(tests[t.sn].running)
  100. t.status = 'running'
  101. else if(tests[t.sn].executed) {
  102. t.status = foundActions ? (pass ? 'pass' : 'fail') : 'skipped'
  103. t.status = t.expired ? 'timeout' : t.status
  104. }
  105. else
  106. t.status = 'waiting'
  107. return (
  108. <View key={'rn-test-' + t.desc} style={{
  109. borderBottomWidth : 1.5,
  110. borderColor : '#DDD',
  111. }}>
  112. <View key={t.desc} style={{
  113. alignItems : 'center',
  114. flexDirection : 'row'
  115. }}>
  116. <Text style={[styles.badge, {flex : 1, borderWidth : 0, textAlign : 'left'}]}>{t.desc}</Text>
  117. <Text style={[styles.badge, this.getBadge(t.status)]}>{t.status}</Text>
  118. </View>
  119. <View key={t.desc + '-result'} style={{backgroundColor : '#F4F4F4'}}>
  120. {t.expand ? t.result : (t.status === 'pass' ? null : t.result)}
  121. </View>
  122. </View>)
  123. }
  124. updateDataSource() {
  125. this.tests = {
  126. common : []
  127. }
  128. this.testGroups = ['common']
  129. RNTEST.TestContext.getTests().forEach((t) => {
  130. if(t.group) {
  131. if(!this.tests[t.group]) {
  132. this.testGroups.push(t.group)
  133. this.tests[t.group] = []
  134. }
  135. this.tests[t.group].push(t)
  136. }
  137. else
  138. this.tests.common.push(t)
  139. })
  140. let listDataSource = new ListView.DataSource({
  141. rowHasChanged : (r1, r2) => r1 !== r2,
  142. sectionHeaderHasChanged: (s1, s2) => s1 !== s2
  143. })
  144. this.ds = listDataSource.cloneWithRowsAndSections(this.tests, this.testGroups)
  145. }
  146. getBadge(status: 'waiting' | 'running' | 'pass' | 'fail' | 'timeout') {
  147. return styles[status]
  148. }
  149. }
  150. const styles = StyleSheet.create({
  151. container: {
  152. flex: 1,
  153. },
  154. badge : {
  155. margin : 16,
  156. padding : 4,
  157. borderRadius : 4,
  158. borderWidth : 2,
  159. textAlign : 'center'
  160. },
  161. skipped: {
  162. borderColor : '#AAAAAA',
  163. color : '#AAAAAA'
  164. },
  165. sectionHeader : {
  166. padding : 16,
  167. backgroundColor : '#F4F4F4',
  168. },
  169. waiting: {
  170. borderColor : '#AAAAAA',
  171. color : '#AAAAAA'
  172. },
  173. pass: {
  174. borderColor : '#00a825',
  175. color : '#00a825'
  176. },
  177. running: {
  178. borderColor : '#e3c423',
  179. color : '#e3c423'
  180. },
  181. fail: {
  182. borderColor : '#ff0d0d',
  183. color : '#ff0d0d'
  184. },
  185. timeout: {
  186. borderColor : '#ff0d0d',
  187. color : '#ff0d0d'
  188. }
  189. });