No Description

test-context.js 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. //@flow
  2. let tests: Array<TestCase> = []
  3. let RCTContext: ReactElement = null
  4. let props:any = {}
  5. let timeout = 30000
  6. let summary = {}
  7. export default class TestContext {
  8. static setTimeout (val) {
  9. timeout = val
  10. }
  11. static config(config) {
  12. return TestContext.describe.bind(config)
  13. }
  14. /**
  15. * Calling this method will push a test case into task queue.
  16. * @param {String} desc Description of test case.
  17. * @param {Function:Promise<any>} fn Body of test case, this function
  18. * should return a promise.
  19. * @return {void}
  20. */
  21. static describe (...args) {
  22. let { group, timeout, expand, run } = this || {}
  23. let desc, config, fn
  24. if([...args].length === 2) {
  25. [desc, fn] = [...args]
  26. }
  27. else if ([...args].length === 3) {
  28. [desc, config, fn] = [...args]
  29. group = config.group || group
  30. timeout = config.timeout || timeout
  31. expand = config.expand || expand
  32. run = config.run || run
  33. }
  34. let ctx = {
  35. group : group || 'common',
  36. status : 'waiting',
  37. run : run === false ? false : true,
  38. result : null,
  39. asserts : [],
  40. timeout : timeout || 15000,
  41. expired : false,
  42. running : false,
  43. executed : false,
  44. expand : expand || false,
  45. desc,
  46. fn,
  47. sn : tests.length,
  48. start : (i) => {
  49. TestContext.startTest.bind(
  50. tests[i],
  51. TestContext.update.bind(TestContext, i),
  52. TestContext.updateInternal.bind(TestContext, i)
  53. )()
  54. }
  55. }
  56. tests.push(ctx)
  57. }
  58. static prop (name:string, val:any):TestContext {
  59. if(name === undefined && val === undefined)
  60. return props
  61. if(val === undefined)
  62. return props[name]
  63. props[name] = val
  64. return TestContext
  65. }
  66. /**
  67. * Run test cases in sequence.
  68. * @param {ReactElement} context ReactElement instance context.
  69. * @return {void}
  70. */
  71. static run (context:ReactElement) {
  72. RCTContext = context
  73. let promise = Promise.resolve()
  74. // run test case sequently
  75. for(let i in tests) {
  76. if(tests[i].run === false) {
  77. tests[i].status = 'skipped'
  78. tests[i].executed = true
  79. promise = Promise.resolve()
  80. continue
  81. }
  82. promise = promise.then(
  83. TestContext.startTest.bind(
  84. tests[i],
  85. TestContext.update.bind(TestContext, i),
  86. TestContext.updateInternal.bind(TestContext, i)
  87. ))
  88. }
  89. return promise
  90. }
  91. static startTest(update, updateInternal, data) {
  92. return new Promise((resolve, reject) => {
  93. let expired = false
  94. updateInternal({
  95. running : true,
  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. // run test body
  107. new Promise((done) => {
  108. try {
  109. this.fn.bind(this)(update, done)
  110. } catch(err) {
  111. console.warn(err.stack)
  112. }
  113. })
  114. .then((...res) => {
  115. if(!expired) {
  116. clearTimeout(tm)
  117. updateInternal({
  118. executed : true,
  119. running : false
  120. })
  121. resolve(...res)
  122. }
  123. RCTContext.forceUpdate()
  124. }).catch((err) => {
  125. updateInternal({
  126. executed : true,
  127. running : false
  128. })
  129. })
  130. })
  131. }
  132. /**
  133. * Update test task result of given index.
  134. * @param {number} i Index of test case to be updated.
  135. * @param {ReactElement<Info | Assert>} ...data Assertion or Info of test.
  136. * @return {void}
  137. */
  138. static update(i, ...data) {
  139. let test = tests[i]
  140. let result = test.result || []
  141. // if new element have prop `uid`, we should replace it not appending it.
  142. for(let i in data) {
  143. if(data[i].props.uid) {
  144. for(let j in result) {
  145. if(result[j].uid === data[i].props.uid)
  146. result[j] = data[i]
  147. result.splice(j,1)
  148. break
  149. }
  150. }
  151. }
  152. Object.assign(test, {result : [...result, ...data]})
  153. RCTContext.forceUpdate()
  154. }
  155. static getTests() {
  156. return tests
  157. }
  158. /**
  159. * Update test result for testkit internal use
  160. * @param {[type]} i Index of test case to be updated.
  161. * @param {TestCaseContext} result Test case object
  162. * @return {void}
  163. */
  164. static updateInternal(i, result) {
  165. Object.assign(tests[i], result)
  166. RCTContext.forceUpdate()
  167. }
  168. }