transition.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. describe('Transition', function () {
  2. var transition = require('vue/src/transition'),
  3. config = require('vue/src/config'),
  4. codes = transition.codes,
  5. endEvents = transition.sniff(),
  6. enterClass = config.enterClass,
  7. leaveClass = config.leaveClass,
  8. nextTick = Vue.nextTick
  9. describe('General', function () {
  10. it('should skip if compiler is in init stage', function () {
  11. var c = mockChange(),
  12. compiler = mockCompiler()
  13. compiler.init = true
  14. var code = transition(null, 1, c.change, compiler)
  15. assert.ok(c.called)
  16. assert.strictEqual(code, codes.INIT)
  17. assert.ok(compiler.attached)
  18. })
  19. it('should skip if no transition is found on the node', function () {
  20. var c = mockChange(),
  21. compiler = mockCompiler(),
  22. code = transition(mockEl(), 1, c.change, compiler)
  23. assert.ok(c.called)
  24. assert.strictEqual(code, codes.SKIP)
  25. assert.ok(compiler.attached)
  26. })
  27. })
  28. describe('CSS Transitions', function () {
  29. if (!endEvents.trans) { // IE9 only test case
  30. it('should skip if transition is not available', function () {
  31. var c = mockChange(),
  32. compiler = mockCompiler(),
  33. code = transition(mockEl('css'), 1, c.change, compiler)
  34. assert.ok(c.called)
  35. assert.strictEqual(code, codes.CSS_SKIP)
  36. assert.ok(compiler.attached)
  37. })
  38. // skip the rest
  39. return
  40. }
  41. describe('enter: transition', function () {
  42. var el = mockEl('css'),
  43. c = mockChange(function () {
  44. c.called = true
  45. assert.ok(el.classList.contains(enterClass))
  46. }),
  47. compiler = mockCompiler(),
  48. code,
  49. cbCalled = false
  50. el.vue_trans_cb = function () {
  51. cbCalled = true
  52. }
  53. el.addEventListener(endEvents.trans, el.vue_trans_cb)
  54. it('should add the class before calling changeState()', function () {
  55. code = transition(el, 1, c.change, compiler)
  56. assert.ok(c.called)
  57. })
  58. it('should remove unfinished leave callback if exists', function () {
  59. assert.notOk(el.vue_trans_cb)
  60. var e = mockHTMLEvent(endEvents.trans)
  61. el.dispatchEvent(e)
  62. assert.notOk(cbCalled)
  63. })
  64. it('should remove the v-leave class if the leave callback exists', function () {
  65. var el = mockEl('css')
  66. document.body.appendChild(el)
  67. el.style.width = '1px'
  68. code = transition(el, -1, function(){}, compiler)
  69. code = transition(el, 1, function(){}, compiler)
  70. assert.notOk(el.classList.contains(leaveClass))
  71. })
  72. it('should remove the class afterwards', function (done) {
  73. nextTick(function () {
  74. assert.notOk(el.classList.contains(enterClass))
  75. done()
  76. })
  77. })
  78. it('should return correct code', function () {
  79. assert.strictEqual(code, codes.CSS_E)
  80. })
  81. it('should have called attached hook', function () {
  82. assert.ok(compiler.attached)
  83. })
  84. })
  85. describe('enter: animation', function () {
  86. var el = mockEl('css'),
  87. c = mockChange(function () {
  88. c.called = true
  89. assert.ok(el.classList.contains(enterClass))
  90. }),
  91. compiler = mockCompiler(),
  92. code
  93. // mark it to use CSS animation instead of transition
  94. el.vue_anim = ''
  95. before(function () {
  96. document.body.appendChild(el)
  97. })
  98. after(function () {
  99. document.body.removeChild(el)
  100. })
  101. it('should add enterClass before calling changeState()', function () {
  102. code = transition(el, 1, c.change, compiler)
  103. assert.ok(c.called)
  104. })
  105. it('should still have the class on nextTick', function (done) {
  106. nextTick(function () {
  107. assert.ok(el.classList.contains(enterClass))
  108. done()
  109. })
  110. })
  111. it('should remove the class when the animation is done', function (done) {
  112. el.addEventListener(endEvents.anim, function () {
  113. assert.notOk(el.vue_trans_cb)
  114. assert.notOk(el.classList.contains(enterClass))
  115. done()
  116. })
  117. var e = mockHTMLEvent(endEvents.anim)
  118. el.dispatchEvent(e)
  119. })
  120. })
  121. describe('leave', function () {
  122. var el = mockEl('css'),
  123. c = mockChange(),
  124. compiler = mockCompiler(),
  125. code
  126. before(function () {
  127. document.body.appendChild(el)
  128. })
  129. after(function () {
  130. document.body.removeChild(el)
  131. })
  132. it('should call change immediately if el is invisible', function () {
  133. var el = mockEl('css'),
  134. c = mockChange(),
  135. compiler = mockCompiler()
  136. code = transition(el, -1, c.change, compiler)
  137. assert.ok(c.called)
  138. assert.ok(compiler.detached)
  139. })
  140. it('should attach an ontransitionend listener', function () {
  141. el.style.width = '1px'
  142. code = transition(el, -1, c.change, compiler)
  143. assert.ok(typeof el.vue_trans_cb === 'function')
  144. })
  145. it('should add the class', function (done) {
  146. nextTick(function () {
  147. assert.ok(el.classList.contains(leaveClass))
  148. done()
  149. })
  150. })
  151. it('should call changeState on transitionend', function () {
  152. var e = mockHTMLEvent(endEvents.trans)
  153. el.dispatchEvent(e)
  154. assert.ok(c.called)
  155. })
  156. it('should remove the callback after called', function () {
  157. assert.notOk(el.vue_trans_cb)
  158. var e = mockHTMLEvent(endEvents.trans)
  159. el.dispatchEvent(e)
  160. assert.strictEqual(c.n, 1)
  161. })
  162. it('should remove the class after called', function () {
  163. assert.notOk(el.classList.contains(leaveClass))
  164. })
  165. it('should return correct code', function () {
  166. assert.strictEqual(code, codes.CSS_L)
  167. })
  168. it('should have called detached hook', function () {
  169. assert.ok(compiler.detached)
  170. })
  171. })
  172. })
  173. describe('JavaScript Transitions', function () {
  174. it('should skip if correspinding option is not defined', function () {
  175. var c = mockChange(),
  176. compiler = mockCompiler(),
  177. code = transition(mockEl('js'), 1, c.change, compiler)
  178. assert.ok(c.called)
  179. assert.strictEqual(code, codes.JS_SKIP)
  180. assert.ok(compiler.attached)
  181. })
  182. it('should skip if the option is given but the enter/leave func is not defined', function () {
  183. var c = mockChange(),
  184. compiler = mockCompiler({}),
  185. code = transition(mockEl('js'), 1, c.change, compiler)
  186. assert.ok(c.called)
  187. assert.strictEqual(code, codes.JS_SKIP_E)
  188. assert.ok(compiler.attached)
  189. c = mockChange()
  190. compiler = mockCompiler({})
  191. code = transition(mockEl('js'), -1, c.change, compiler)
  192. assert.ok(c.called)
  193. assert.strictEqual(code, codes.JS_SKIP_L)
  194. assert.ok(compiler.detached)
  195. })
  196. describe('enter', function () {
  197. var code,
  198. c = mockChange(),
  199. el = mockEl('js'),
  200. def = {
  201. enter: function (element, change) {
  202. assert.strictEqual(el, element)
  203. change()
  204. }
  205. },
  206. compiler = mockCompiler(def)
  207. it('should call the enter function', function () {
  208. code = transition(el, 1, c.change, compiler)
  209. assert.ok(c.called)
  210. })
  211. it('should return correct code', function () {
  212. assert.strictEqual(code, codes.JS_E)
  213. })
  214. it('should have called attached hook', function () {
  215. assert.ok(compiler.attached)
  216. })
  217. })
  218. describe('leave', function () {
  219. var code,
  220. c = mockChange(),
  221. el = mockEl('js'),
  222. def = {
  223. leave: function (element, change) {
  224. assert.strictEqual(el, element)
  225. change()
  226. }
  227. },
  228. compiler = mockCompiler(def)
  229. it('should call the leave function', function () {
  230. code = transition(el, -1, c.change, compiler)
  231. assert.ok(c.called)
  232. })
  233. it('should return correct code', function () {
  234. assert.strictEqual(code, codes.JS_L)
  235. })
  236. it('should have called detached hook', function () {
  237. assert.ok(compiler.detached)
  238. })
  239. })
  240. describe('wrapped timeout', function () {
  241. var el = mockEl('js'),
  242. c = mockChange(),
  243. timerFired = false,
  244. def = {
  245. enter: function (el, change, timeout) {
  246. change()
  247. timeout(function () {
  248. timerFired = true
  249. }, 0)
  250. },
  251. leave: function () {}
  252. },
  253. compiler = mockCompiler(def)
  254. it('should cancel previous unfired timers', function (done) {
  255. transition(el, 1, c.change, compiler)
  256. assert.strictEqual(el.vue_timeouts.length, 1)
  257. transition(el, -1, c.change, compiler)
  258. assert.strictEqual(el.vue_timeouts.length, 0)
  259. setTimeout(function () {
  260. assert.notOk(timerFired)
  261. done()
  262. }, 0)
  263. })
  264. })
  265. })
  266. function mockChange (change) {
  267. var c = {
  268. called: false,
  269. n: 0,
  270. change: change || function () {
  271. c.called = true
  272. c.n += 1
  273. }
  274. }
  275. return c
  276. }
  277. function mockEl (type) {
  278. var el = document.createElement('div')
  279. if (type === 'css') {
  280. el.vue_trans = ''
  281. } else if (type === 'js') {
  282. el.vue_effect = 'test'
  283. }
  284. return el
  285. }
  286. function mockCompiler (opt) {
  287. return {
  288. getOption: function () {
  289. return opt
  290. },
  291. execHook: function (hook) {
  292. this[hook] = true
  293. }
  294. }
  295. }
  296. })