compile_spec.js 6.8 KB

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