瀏覽代碼

Add config API to testkit

Ben Hsieh 8 年之前
父節點
當前提交
c0a9990538

+ 97
- 39
test/react-native-testkit/components/reporter.js 查看文件

@@ -6,7 +6,10 @@ import {
6 6
   View,
7 7
   Platform,
8 8
   ScrollView,
9
+  ListView,
9 10
   Image,
11
+  TouchableOpacity,
12
+  RecyclerViewBackedScrollView,
10 13
 } from 'react-native';
11 14
 
12 15
 import Assert from './assert.js'
@@ -14,55 +17,107 @@ import RNTEST from '../index.js'
14 17
 
15 18
 export default class Reporter extends Component {
16 19
 
20
+  constructor(props:any) {
21
+    super(props)
22
+    this.tests = {
23
+      common : []
24
+    }
25
+    this.testGroups = ['common']
26
+    this.ds = null
27
+    this.updateDataSource()
28
+
29
+  }
30
+
31
+  componentWillUpdate(nextProps, nextState) {
32
+    this.updateDataSource()
33
+  }
34
+
17 35
   render() {
36
+
18 37
     return (
19
-      <ScrollView key="rn-test-scroller" style={styles.container}>
20
-        {this.renderTests()}
21
-      </ScrollView>)
38
+      <ListView
39
+        style={styles.container}
40
+        dataSource={this.ds}
41
+        renderRow={this.renderTest.bind(this)}
42
+        renderScrollComponent={props => <RecyclerViewBackedScrollView {...props} />}
43
+        renderSectionHeader={(data, id) => {
44
+          return (
45
+            <View style={styles.sectionHeader}>
46
+              <Text style={styles.sectionText}>{id}</Text>
47
+            </View>
48
+          )
49
+        }}
50
+      />)
22 51
   }
23 52
 
24
-  renderTests() {
53
+  renderTest(t) {
54
+    let pass = true
55
+    let foundActions = false
25 56
     let tests = RNTEST.TestContext.getTests()
26
-    return tests.map((t, i) => {
27 57
 
28
-      let pass = true
29
-      let foundActions = false
58
+    if(Array.isArray(t.result) && !t.expired) {
59
+      t.result = t.result.map((r) => {
60
+        if(r.type.name === 'Assert' || r.type.name === 'Info') {
61
+          foundActions = true
62
+          let comp = r.props.comparer ? r.props.comparer(r.props.expect, r.props.actual) : (r.props.actual === r.props.expect)
63
+          pass = pass && comp
64
+        }
65
+        return React.cloneElement(r, {desc : r.key})
66
+      })
67
+    }
68
+    if(tests[t.sn].running)
69
+      t.status = 'running'
70
+    else if(tests[t.sn].executed) {
71
+      t.status = foundActions ? (pass ? 'pass' : 'fail') : 'skipped'
72
+      t.status = t.expired ? 'timeout' : t.status
73
+    }
74
+    else
75
+      t.status = 'waiting'
30 76
 
31
-      if(Array.isArray(t.result) && !t.expired) {
32
-        t.result = t.result.map((r) => {
33
-          if(r.type.name === 'Assert' || r.type.name === 'Info') {
34
-            foundActions = true
35
-            let comp = r.props.comparer ? r.props.comparer(r.props.expect, r.props.actual) : (r.props.actual === r.props.expect)
36
-            pass = pass && comp
37
-          }
38
-          return React.cloneElement(r, {desc : r.key})
39
-        })
40
-      }
41
-      if(tests[i].running)
42
-        t.status = 'running'
43
-      else if(tests[i].executed) {
44
-        t.status = foundActions ? (pass ? 'pass' : 'fail') : 'skipped'
45
-        t.status = t.expired ? 'timeout' : t.status
77
+    return (
78
+      <TouchableOpacity onPress={()=>{
79
+          t.start(t.sn)
80
+        }}>
81
+        <View key={'rn-test-' + t.desc} style={{
82
+          borderBottomWidth : 1.5,
83
+          borderColor : '#DDD',
84
+        }}>
85
+          <View key={t.desc} style={{
86
+            alignItems : 'center',
87
+            flexDirection : 'row'
88
+          }}>
89
+            <Text style={[styles.badge, {flex : 1, borderWidth : 0, textAlign : 'left'}]}>{t.desc}</Text>
90
+            <Text style={[styles.badge, this.getBadge(t.status)]}>{t.status}</Text>
91
+          </View>
92
+          <View key={t.desc + '-result'} style={{backgroundColor : '#F4F4F4'}}>
93
+            {t.expand ? t.result : (t.status === 'pass' ? null : t.result)}
94
+          </View>
95
+        </View>
96
+      </TouchableOpacity>)
97
+  }
98
+
99
+  updateDataSource() {
100
+    this.tests = {
101
+      common : []
102
+    }
103
+    this.testGroups = ['common']
104
+    RNTEST.TestContext.getTests().forEach((t) => {
105
+      if(t.group) {
106
+        if(!this.tests[t.group]) {
107
+          this.testGroups.push(t.group)
108
+          this.tests[t.group] = []
109
+        }
110
+        this.tests[t.group].push(t)
46 111
       }
47 112
       else
48
-        t.status = 'waiting'
113
+        this.tests.common.push(t)
114
+    })
49 115
 
50
-      return (<View key={'rn-test-' + t.desc} style={{
51
-        borderBottomWidth : 1.5,
52
-        borderColor : '#DDD',
53
-      }}>
54
-        <View key={t.desc} style={{
55
-          alignItems : 'center',
56
-          flexDirection : 'row'
57
-        }}>
58
-          <Text style={[styles.badge, {flex : 1, borderWidth : 0, textAlign : 'left'}]}>{t.desc}</Text>
59
-          <Text style={[styles.badge, this.getBadge(t.status)]}>{t.status}</Text>
60
-        </View>
61
-        <View key={t.desc + '-result'} style={{backgroundColor : '#F4F4F4'}}>
62
-          {t.result}
63
-        </View>
64
-      </View>)
116
+    let listDataSource = new ListView.DataSource({
117
+      rowHasChanged : (r1, r2) => r1 !== r2,
118
+      sectionHeaderHasChanged: (s1, s2) => s1 !== s2
65 119
     })
120
+    this.ds = listDataSource.cloneWithRowsAndSections(this.tests, this.testGroups)
66 121
   }
67 122
 
68 123
   getBadge(status: 'waiting' | 'running' | 'pass' | 'fail' | 'timeout') {
@@ -74,7 +129,6 @@ export default class Reporter extends Component {
74 129
 const styles = StyleSheet.create({
75 130
   container: {
76 131
     flex: 1,
77
-    marginTop : 40,
78 132
   },
79 133
   badge : {
80 134
     margin : 16,
@@ -87,6 +141,10 @@ const styles = StyleSheet.create({
87 141
     borderColor : '#AAAAAA',
88 142
     color : '#AAAAAA'
89 143
   },
144
+  sectionHeader : {
145
+    padding : 16,
146
+    backgroundColor : '#F4F4F4',
147
+  },
90 148
   waiting: {
91 149
     borderColor : '#AAAAAA',
92 150
     color : '#AAAAAA'

+ 2
- 1
test/react-native-testkit/index.js 查看文件

@@ -4,13 +4,14 @@ import Reporter from './components/reporter'
4 4
 import Assert from './components/assert'
5 5
 import Info from './components/info'
6 6
 
7
-const { describe, run, prop } = TestContext
7
+const { describe, run, prop, config } = TestContext
8 8
 
9 9
 export default {
10 10
   TestContext,
11 11
   Reporter,
12 12
   Info,
13 13
   Assert,
14
+  config,
14 15
   Comparer,
15 16
   describe,
16 17
   run,

+ 7
- 0
test/react-native-testkit/lib/comparer.js 查看文件

@@ -5,6 +5,13 @@ export default {
5 5
   typeof : (a, b) => typeof a === b,
6 6
   IsNull : (a, b) => a === null,
7 7
   exists : (a, b) => a,
8
+  equalToArray : (a, b) => {
9
+    if(!Array.isArray(a) && Array.isArray(b))
10
+      return false
11
+    return (a.length == b.length) && a.every(function(element, index) {
12
+      return element === b[index];
13
+    });
14
+  },
8 15
   hasValue : (a, b) => (a !== void 0) && (Array.isArray(a) ? a.length !==0 : true),
9 16
   isArray : (a, b) => Array.isArray(a),
10 17
   hasProperties : (a, b) => {

+ 73
- 47
test/react-native-testkit/lib/test-context.js 查看文件

@@ -11,6 +11,10 @@ export default class TestContext {
11 11
     timeout = val
12 12
   }
13 13
 
14
+  static config(config) {
15
+    return TestContext.describe.bind(config)
16
+  }
17
+
14 18
   /**
15 19
    * Calling this method will push a test case into task queue.
16 20
    * @param  {String}   desc Description of test case.
@@ -19,17 +23,31 @@ export default class TestContext {
19 23
    * @return {void}
20 24
    */
21 25
   static describe (desc:string, fn:Promise<any>) {
26
+    let { group, timeout, expand, run } = this || {}
22 27
 
23
-    tests.push({
28
+    let ctx = {
29
+      group : group || 'common',
24 30
       status : 'waiting',
31
+      run : run === false ? false : true,
25 32
       result : null,
26 33
       asserts : [],
27
-      timeout : 3000,
34
+      timeout : timeout || 3000,
28 35
       expired : false,
29 36
       running : false,
30 37
       executed : false,
31
-      desc, fn,
32
-    })
38
+      expand : expand || false,
39
+      desc,
40
+      fn,
41
+      sn : tests.length,
42
+      start : (i) => {
43
+        TestContext.startTest.bind(
44
+          tests[i],
45
+          TestContext.update.bind(TestContext, i),
46
+          TestContext.updateInternal.bind(TestContext, i)
47
+        )()
48
+      }
49
+    }
50
+    tests.push(ctx)
33 51
 
34 52
   }
35 53
 
@@ -52,55 +70,63 @@ export default class TestContext {
52 70
     let promise = Promise.resolve()
53 71
     // run test case sequently
54 72
     for(let i in tests) {
55
-      promise = promise.then(function(update, updateInternal, data) {
56
-        return new Promise((resolve, reject) => {
57
-
58
-          let expired = false
59
-          updateInternal({
60
-            running : true,
61
-          })
62
-
63
-          // set timeout timer
64
-          let tm = setTimeout(() => {
65
-            updateInternal({
66
-              expired : true,
67
-              executed : true,
68
-              running : false
69
-            })
70
-            resolve('ETIMEOUT')
71
-          }, this.timeout)
72
-
73
-          // run test body
74
-          new Promise((done) => {
75
-            this.fn.bind(this)(update, done)
76
-          })
77
-          .then((...res) => {
78
-            if(!expired) {
79
-              clearTimeout(tm)
80
-              updateInternal({
81
-                executed : true,
82
-                running : false
83
-              })
84
-              resolve(...res)
85
-            }
86
-          }).catch((err) => {
87
-            updateInternal({
88
-              executed : true,
89
-              running : false
90
-            })
91
-          })
92
-
93
-        })
73
+      if(tests[i].run === false) {
74
+        tests[i].status = 'skipped'
75
+        tests[i].executed = true
76
+        promise = Promise.resolve()
77
+        continue
94 78
       }
95
-      .bind(
96
-        tests[i],
97
-        TestContext.update.bind(TestContext, i),
98
-        TestContext.updateInternal.bind(TestContext, i)
79
+      promise = promise.then(
80
+        TestContext.startTest.bind(
81
+          tests[i],
82
+          TestContext.update.bind(TestContext, i),
83
+          TestContext.updateInternal.bind(TestContext, i)
99 84
       ))
100 85
     }
101 86
     return promise
102 87
   }
103 88
 
89
+  static startTest(update, updateInternal, data) {
90
+    return new Promise((resolve, reject) => {
91
+
92
+      let expired = false
93
+      updateInternal({
94
+        running : true,
95
+      })
96
+
97
+      // set timeout timer
98
+      let tm = setTimeout(() => {
99
+        updateInternal({
100
+          expired : true,
101
+          executed : true,
102
+          running : false
103
+        })
104
+        resolve('ETIMEOUT')
105
+      }, this.timeout)
106
+
107
+      // run test body
108
+      new Promise((done) => {
109
+        this.fn.bind(this)(update, done)
110
+      })
111
+      .then((...res) => {
112
+        if(!expired) {
113
+          clearTimeout(tm)
114
+          updateInternal({
115
+            executed : true,
116
+            running : false
117
+          })
118
+          resolve(...res)
119
+        }
120
+      }).catch((err) => {
121
+        updateInternal({
122
+          executed : true,
123
+          running : false
124
+        })
125
+      })
126
+
127
+    })
128
+  }
129
+
104 130
   /**
105 131
    * Update test task result of given index.
106 132
    * @param  {number} i       Index of test case to be updated.

+ 6
- 1
test/test-0.1.x-0.4.x.js 查看文件

@@ -10,7 +10,12 @@ import {
10 10
   Dimensions,
11 11
   Image,
12 12
 } from 'react-native';
13
-const { Assert, Comparer, Info, describe, prop } = RNTest
13
+const { Assert, Comparer, Info, prop } = RNTest
14
+const describe = RNTest.config({
15
+  group : '0.1.x - 0.4.x',
16
+  expand : false,
17
+  run : false
18
+})
14 19
 
15 20
 let { TEST_SERVER_URL, FILENAME, DROPBOX_TOKEN, styles, image } = prop()
16 21
 

+ 102
- 3
test/test-0.5.x.js 查看文件

@@ -12,7 +12,11 @@ import {
12 12
   Image,
13 13
 } from 'react-native';
14 14
 
15
-const { Assert, Comparer, Info, describe, prop } = RNTest
15
+const { Assert, Comparer, Info, prop } = RNTest
16
+const describe = RNTest.config({
17
+  group : '0.5.x',
18
+  expand : false,
19
+})
16 20
 const { TEST_SERVER_URL, FILENAME, DROPBOX_TOKEN, styles } = prop()
17 21
 
18 22
 let prefix = ((Platform.OS === 'android') ? 'file://' : '')
@@ -60,7 +64,7 @@ describe('Download file to storage with custom file extension', (report, done) =
60 64
 
61 65
 describe('Read cached file via file stream', (report, done) => {
62 66
   let data = 'data:image/png;base64, '
63
-  let stream = RNFetchBlob.openReadStream(tmpFilePath, 'base64')
67
+  let stream = RNFetchBlob.readStream(tmpFilePath, 'base64')
64 68
   stream.onData((chunk) => {
65 69
     data += chunk
66 70
   })
@@ -81,7 +85,7 @@ describe('Read cached file via file stream', (report, done) => {
81 85
 })
82 86
 
83 87
 describe('File stream reader error should be able to handled', (report, done) => {
84
-  let stream = RNFetchBlob.openReadStream('^_^ not exists', 'base64')
88
+  let stream = RNFetchBlob.readStream('^_^ not exists', 'base64')
85 89
   stream.onError((err) => {
86 90
     report(<Info key="error message">
87 91
       <Text>
@@ -94,11 +98,13 @@ describe('File stream reader error should be able to handled', (report, done) =>
94 98
 })
95 99
 
96 100
 let localFile = null
101
+let sysDirs = null
97 102
 
98 103
 describe('Upload from file storage', (report, done) => {
99 104
   let filename = ''
100 105
   let filepath = ''
101 106
   RNFetchBlob.getSystemDirs().then((dirs) => {
107
+    sysDirs = dirs
102 108
     filename = Platform.OS + '0.5.0-' + Date.now() + '-from-storage.png'
103 109
     filepath = dirs.DocumentDir + '/' + filename
104 110
     return RNFetchBlob.config({ path : filepath })
@@ -148,5 +154,98 @@ describe('Upload multipart data with file from storage', (report, done) => {
148 154
       </Info>)
149 155
       done()
150 156
     })
157
+})
158
+
159
+describe('Session create mechanism test', (report, done) => {
160
+  let sessionName = 'foo-' + Date.now()
161
+  testSessionName = sessionName
162
+  let p1 = RNFetchBlob.config({
163
+      session : sessionName,
164
+      fileCache : true
165
+    })
166
+    .fetch('GET', `${TEST_SERVER_URL}/public/github2.jpg`)
167
+  let p2 = RNFetchBlob.config({
168
+      fileCache : true
169
+    })
170
+    .fetch('GET', `${TEST_SERVER_URL}/public/github.png`)
171
+  let p3 = RNFetchBlob.config({
172
+      path : sysDirs.DocumentDir + '/session-test.png'
173
+    })
174
+    .fetch('GET', `${TEST_SERVER_URL}/public/github.png`)
175
+
176
+  let promises = [p1, p2, p3]
177
+  Promise.all(promises).then((resp) => {
178
+    let session = RNFetchBlob.session(sessionName).add(resp[1].path())
179
+    resp[2].session(sessionName)
180
+    let actual = session.list()
181
+    let expect = resp.map((p) => {
182
+      return p.path()
183
+    })
184
+    report(
185
+      <Assert key="check if session state correct"
186
+        expect={expect}
187
+        comparer={Comparer.equalToArray}
188
+        actual={actual} />)
189
+    done()
190
+  })
191
+
192
+})
193
+
194
+describe('Session API CRUD test', (report, done) => {
195
+
196
+  let sessionName = 'test-session-' + Date.now()
197
+  let baseDir = sysDirs.DocumentDir + '/' + sessionName
198
+  RNFetchBlob.mkdir(sysDirs.DocumentDir + '/' + sessionName).then(() => {
199
+    let promises = [0,1,2,3,4,5,6,7,8,9].map((p) => {
200
+      return RNFetchBlob.config({
201
+          session : sessionName,
202
+          path : baseDir + '/testfile' + p
203
+        })
204
+        .fetch('GET', `${TEST_SERVER_URL}/public/github2.jpg`)
205
+    })
206
+    return Promise.all(promises)
207
+  })
208
+  .then((resps) => {
209
+    let s = RNFetchBlob.session(sessionName)
210
+    report(
211
+      <Assert
212
+        key="list() length validation"
213
+        expect={10}
214
+        actual={s.list().length}/>)
215
+    let modified = [
216
+      s.list()[2],
217
+      s.list()[3],
218
+      s.list()[4],
219
+      s.list()[5],
220
+      s.list()[6],
221
+      s.list()[7],
222
+      s.list()[8],
223
+      s.list()[9],
224
+    ]
225
+    let expect = [s.list()[0], s.list()[1]]
226
+    s.remove(s.list()[0])
227
+    s.remove(s.list()[0])
228
+    report(
229
+      <Assert
230
+        key="remove() should work correctly"
231
+        expect={modified}
232
+        comparer={Comparer.equalToArray}
233
+        actual={s.list()}/>)
234
+
235
+    s.dispose().then(() => {
236
+      RNFetchBlob.ls(baseDir).then((lsRes) => {
237
+        report(
238
+          <Assert
239
+            key="dispose() should work correctly"
240
+            expect={expect}
241
+            comparer={Comparer.equalToArray}
242
+            actual={lsRes.map((p) => {
243
+              return baseDir + '/' + p
244
+            })}/>)
245
+      })
246
+      done()
247
+    })
248
+
249
+  })
151 250
 
152 251
 })

+ 2
- 2
test/test-init.js 查看文件

@@ -18,7 +18,7 @@ const { Assert, Comparer, Info, describe, prop } = RNTest
18 18
 // test environment variables
19 19
 
20 20
 prop('FILENAME', `${Platform.OS}-0.5.0-${Date.now()}.png`)
21
-prop('TEST_SERVER_URL', 'http://192.168.17.193:8123')
21
+prop('TEST_SERVER_URL', 'http://192.168.0.14:8123')
22 22
 prop('DROPBOX_TOKEN', 'fsXcpmKPrHgAAAAAAAAAoXZhcXYWdgLpQMan6Tb_bzJ237DXhgQSev12hA-gUXt4')
23 23
 prop('styles', {
24 24
   image : {
@@ -51,5 +51,5 @@ describe('GET image from server', (report, done) => {
51 51
     })
52 52
 })
53 53
 
54
-// require('./test-0.1.x-0.4.x')
54
+require('./test-0.1.x-0.4.x')
55 55
 require('./test-0.5.x')