compile_spec.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. var Vue = require('../../../../src/vue')
  2. var _ = require('../../../../src/util')
  3. var dirParser = require('../../../../src/parse/directive')
  4. var merge = require('../../../../src/util/merge-option')
  5. var compile = require('../../../../src/compile/compile')
  6. if (_.inBrowser) {
  7. describe('Compile', function () {
  8. var vm, el, data, directiveTeardown
  9. beforeEach(function () {
  10. // We mock vms here so we can assert what the generated
  11. // linker functions do.
  12. el = document.createElement('div')
  13. data = {}
  14. directiveTeardown = jasmine.createSpy()
  15. vm = {
  16. _directives: [],
  17. _bindDir: function () {
  18. this._directives.push({
  19. _teardown: directiveTeardown
  20. })
  21. },
  22. $set: jasmine.createSpy(),
  23. $eval: function (value) {
  24. return data[value]
  25. },
  26. $interpolate: function (value) {
  27. return data[value]
  28. }
  29. }
  30. spyOn(vm, '_bindDir').and.callThrough()
  31. spyOn(vm, '$eval').and.callThrough()
  32. spyOn(vm, '$interpolate').and.callThrough()
  33. spyOn(_, 'warn')
  34. })
  35. it('normal directives', function () {
  36. el.setAttribute('v-a', 'b')
  37. el.innerHTML = '<p v-a="a" v-b="b">hello</p><div v-b="b"></div>'
  38. var defA = { priority: 1 }
  39. var defB = { priority: 2 }
  40. var descriptorA = dirParser.parse('a')[0]
  41. var descriptorB = dirParser.parse('b')[0]
  42. var options = merge(Vue.options, {
  43. directives: {
  44. a: defA,
  45. b: defB
  46. }
  47. })
  48. var linker = compile(el, options)
  49. expect(typeof linker).toBe('function')
  50. linker(vm, el)
  51. expect(vm._bindDir.calls.count()).toBe(4)
  52. expect(vm._bindDir).toHaveBeenCalledWith('a', el, descriptorB, defA)
  53. expect(vm._bindDir).toHaveBeenCalledWith('a', el.firstChild, descriptorA, defA)
  54. expect(vm._bindDir).toHaveBeenCalledWith('b', el.firstChild, descriptorB, defB)
  55. expect(vm._bindDir).toHaveBeenCalledWith('b', el.lastChild, descriptorB, defB)
  56. // check the priority sorting
  57. // the "b" on the firstNode should be called first!
  58. expect(vm._bindDir.calls.argsFor(1)[0]).toBe('b')
  59. })
  60. it('text interpolation', function () {
  61. data.b = 'yeah'
  62. el.innerHTML = '{{a}} and {{*b}}'
  63. var def = Vue.options.directives.text
  64. var linker = compile(el, Vue.options)
  65. linker(vm, el)
  66. // expect 1 call because one-time bindings do not generate a directive.
  67. expect(vm._bindDir.calls.count()).toBe(1)
  68. var args = vm._bindDir.calls.argsFor(0)
  69. expect(args[0]).toBe('text')
  70. // skip the node because it's generated in the linker fn via cloneNode
  71. expect(args[2]).toBe(dirParser.parse('a')[0])
  72. expect(args[3]).toBe(def)
  73. // expect $eval to be called during onetime
  74. expect(vm.$eval).toHaveBeenCalledWith('b')
  75. // {{a}} is mocked so it's a space.
  76. // but we want to make sure {{*b}} worked.
  77. expect(el.innerHTML).toBe(' and yeah')
  78. })
  79. it('inline html and partial', function () {
  80. data.html = 'yoyoyo'
  81. el.innerHTML = '{{{html}}} {{{*html}}} {{>partial}}'
  82. var htmlDef = Vue.options.directives.html
  83. var partialDef = Vue.options.directives.partial
  84. var htmlDesc = dirParser.parse('html')[0]
  85. var partialDesc = dirParser.parse('partial')[0]
  86. var linker = compile(el, Vue.options)
  87. linker(vm, el)
  88. expect(vm._bindDir.calls.count()).toBe(2)
  89. var htmlArgs = vm._bindDir.calls.argsFor(0)
  90. expect(htmlArgs[0]).toBe('html')
  91. expect(htmlArgs[2]).toBe(htmlDesc)
  92. expect(htmlArgs[3]).toBe(htmlDef)
  93. var partialArgs = vm._bindDir.calls.argsFor(1)
  94. expect(partialArgs[0]).toBe('partial')
  95. expect(partialArgs[2]).toBe(partialDesc)
  96. expect(partialArgs[3]).toBe(partialDef)
  97. expect(vm.$eval).toHaveBeenCalledWith('html')
  98. // with placeholder comments & interpolated one-time html
  99. expect(el.innerHTML).toBe('<!--v-html--> yoyoyo <!--v-partial-->')
  100. })
  101. it('terminal directives', function () {
  102. el.innerHTML =
  103. '<div v-repeat="items"><p v-a="b"></p></div>' + // v-repeat
  104. '<div v-pre><p v-a="b"></p></div>' // v-pre
  105. var def = Vue.options.directives.repeat
  106. var descriptor = dirParser.parse('items')[0]
  107. var linker = compile(el, Vue.options)
  108. linker(vm, el)
  109. // expect 1 call because terminal should return early and let
  110. // the directive handle the rest.
  111. expect(vm._bindDir.calls.count()).toBe(1)
  112. expect(vm._bindDir).toHaveBeenCalledWith('repeat', el.firstChild, descriptor, def)
  113. })
  114. it('custom element components', function () {
  115. var options = merge(Vue.options, {
  116. components: {
  117. 'my-component': {}
  118. }
  119. })
  120. el.innerHTML = '<my-component><div v-a="b"></div></my-component>'
  121. var def = Vue.options.directives.component
  122. var descriptor = dirParser.parse('my-component')[0]
  123. var linker = compile(el, options)
  124. linker(vm, el)
  125. expect(vm._bindDir.calls.count()).toBe(1)
  126. expect(vm._bindDir).toHaveBeenCalledWith('component', el.firstChild, descriptor, def)
  127. expect(_.warn).not.toHaveBeenCalled()
  128. })
  129. it('attribute interpolation', function () {
  130. data['{{*b}}'] = 'B'
  131. el.innerHTML = '<div a="{{a}}" b="{{*b}}"></div>'
  132. var def = Vue.options.directives.attr
  133. var descriptor = dirParser.parse('a:(a)')[0]
  134. var linker = compile(el, Vue.options)
  135. linker(vm, el)
  136. expect(vm._bindDir.calls.count()).toBe(1)
  137. expect(vm._bindDir).toHaveBeenCalledWith('attr', el.firstChild, descriptor, def)
  138. expect(el.firstChild.getAttribute('b')).toBe('B')
  139. })
  140. it('param attributes', function () {
  141. var options = merge(Vue.options, {
  142. paramAttributes: ['a', 'b', 'c']
  143. })
  144. var def = Vue.options.directives['with']
  145. el.setAttribute('a', '1')
  146. el.setAttribute('b', '{{a}}')
  147. el.setAttribute('c', 'a {{b}} c') // invalid
  148. var linker = compile(el, options)
  149. linker(vm, el)
  150. // should skip literal & invliad
  151. expect(vm._bindDir.calls.count()).toBe(1)
  152. var args = vm._bindDir.calls.argsFor(0)
  153. expect(args[0]).toBe('with')
  154. expect(args[1]).toBe(el)
  155. // skipping descriptor because it's ducked inline
  156. expect(args[3]).toBe(def)
  157. // invalid should've warn
  158. expect(_.warn).toHaveBeenCalled()
  159. // literal should've called vm.$set
  160. expect(vm.$set).toHaveBeenCalledWith('a', '1')
  161. })
  162. it('DocumentFragment', function () {
  163. var frag = document.createDocumentFragment()
  164. frag.appendChild(el)
  165. var el2 = document.createElement('div')
  166. frag.appendChild(el2)
  167. el.innerHTML = '{{*a}}'
  168. el2.innerHTML = '{{*b}}'
  169. data.a = 'A'
  170. data.b = 'B'
  171. var linker = compile(frag, Vue.options)
  172. linker(vm, frag)
  173. expect(el.innerHTML).toBe('A')
  174. expect(el2.innerHTML).toBe('B')
  175. })
  176. it('partial compilation', function () {
  177. el.innerHTML = '<div v-attr="test:abc">{{bcd}}<p v-show="ok"></p></div>'
  178. var linker = compile(el, Vue.options, true)
  179. var decompile = linker(vm, el)
  180. expect(vm._directives.length).toBe(3)
  181. decompile()
  182. expect(directiveTeardown.calls.count()).toBe(3)
  183. expect(vm._directives.length).toBe(0)
  184. })
  185. })
  186. }