compile_spec.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718
  1. var Vue = require('src')
  2. var _ = require('src/util')
  3. var FragmentFactory = require('src/fragment/factory')
  4. var compiler = require('src/compiler')
  5. var compile = compiler.compile
  6. var publicDirectives = require('src/directives/public')
  7. var internalDirectives = require('src/directives/internal')
  8. describe('Compile', function () {
  9. var vm, el, data, directiveBind, directiveTeardown
  10. beforeEach(function () {
  11. // We mock vms here so we can assert what the generated
  12. // linker functions do.
  13. el = document.createElement('div')
  14. data = {}
  15. directiveBind = jasmine.createSpy('bind')
  16. directiveTeardown = jasmine.createSpy('teardown')
  17. vm = {
  18. $options: {},
  19. _data: {},
  20. _directives: [],
  21. _bindDir: function (descriptor, node) {
  22. this._directives.push({
  23. name: descriptor.name,
  24. descriptor: descriptor,
  25. _bind: function () {
  26. directiveBind(this.name)
  27. },
  28. _teardown: directiveTeardown
  29. })
  30. },
  31. $get: function (exp) {
  32. return (new Vue()).$get(exp)
  33. },
  34. $eval: function (value) {
  35. return data[value]
  36. },
  37. $interpolate: function (value) {
  38. return data[value]
  39. },
  40. _context: {
  41. _directives: [],
  42. $get: function (v) {
  43. return 'from parent: ' + v
  44. }
  45. }
  46. }
  47. spyOn(vm, '_bindDir').and.callThrough()
  48. spyOn(vm, '$eval').and.callThrough()
  49. spyOn(vm, '$interpolate').and.callThrough()
  50. })
  51. it('normal directives', function () {
  52. el.setAttribute('v-a', 'b')
  53. el.innerHTML = '<p v-a:hello.a.b="a" v-b="1">hello</p><div v-b.literal="foo"></div>'
  54. var defA = { priority: 1 }
  55. var defB = { priority: 2 }
  56. var options = _.mergeOptions(Vue.options, {
  57. directives: {
  58. a: defA,
  59. b: defB
  60. }
  61. })
  62. var linker = compile(el, options)
  63. expect(typeof linker).toBe('function')
  64. linker(vm, el)
  65. expect(directiveBind.calls.count()).toBe(4)
  66. expect(vm._bindDir.calls.count()).toBe(4)
  67. // check if we are in firefox, which has different
  68. // attribute interation order
  69. var isAttrReversed = el.firstChild.attributes[0].name === 'v-b'
  70. // 1
  71. var args = vm._bindDir.calls.argsFor(0)
  72. expect(args[0].name).toBe('a')
  73. expect(args[0].expression).toBe('b')
  74. expect(args[0].def).toBe(defA)
  75. expect(args[1]).toBe(el)
  76. // 2
  77. args = vm._bindDir.calls.argsFor(isAttrReversed ? 2 : 1)
  78. expect(args[0].name).toBe('a')
  79. expect(args[0].expression).toBe('a')
  80. expect(args[0].def).toBe(defA)
  81. // args + multiple modifiers
  82. expect(args[0].arg).toBe('hello')
  83. expect(args[0].modifiers.a).toBe(true)
  84. expect(args[0].modifiers.b).toBe(true)
  85. expect(args[1]).toBe(el.firstChild)
  86. // 3 (expression literal)
  87. args = vm._bindDir.calls.argsFor(isAttrReversed ? 1 : 2)
  88. expect(args[0].name).toBe('b')
  89. expect(args[0].expression).toBe('1')
  90. expect(args[0].def).toBe(defB)
  91. expect(args[1]).toBe(el.firstChild)
  92. // 4 (explicit literal)
  93. args = vm._bindDir.calls.argsFor(3)
  94. expect(args[0].name).toBe('b')
  95. expect(args[0].expression).toBe('foo')
  96. expect(args[0].def).toBe(defB)
  97. expect(args[0].modifiers.literal).toBe(true)
  98. expect(args[1]).toBe(el.lastChild)
  99. // check the priority sorting
  100. // the "b"s should be called first!
  101. expect(directiveBind.calls.argsFor(0)[0]).toBe('b')
  102. expect(directiveBind.calls.argsFor(1)[0]).toBe('b')
  103. expect(directiveBind.calls.argsFor(2)[0]).toBe('a')
  104. expect(directiveBind.calls.argsFor(3)[0]).toBe('a')
  105. })
  106. it('v-bind shorthand', function () {
  107. el.setAttribute(':class', 'a')
  108. el.setAttribute(':style', 'b')
  109. el.setAttribute(':title', 'c')
  110. // The order of setAttribute is not guaranteed to be the same with
  111. // the order of attribute enumberation, therefore we need to save
  112. // it here!
  113. var descriptors = {
  114. ':class': {
  115. name: 'class',
  116. attr: ':class',
  117. expression: 'a',
  118. def: internalDirectives.class
  119. },
  120. ':style': {
  121. name: 'style',
  122. attr: ':style',
  123. expression: 'b',
  124. def: internalDirectives.style
  125. },
  126. ':title': {
  127. name: 'bind',
  128. attr: ':title',
  129. expression: 'c',
  130. arg: 'title',
  131. def: publicDirectives.bind
  132. }
  133. }
  134. var expects = [].map.call(el.attributes, function (attr) {
  135. return descriptors[attr.name]
  136. })
  137. var linker = compile(el, Vue.options)
  138. linker(vm, el)
  139. expect(vm._bindDir.calls.count()).toBe(3)
  140. expects.forEach(function (e, i) {
  141. var args = vm._bindDir.calls.argsFor(i)
  142. for (var key in e) {
  143. expect(args[0][key]).toBe(e[key])
  144. }
  145. expect(args[1]).toBe(el)
  146. })
  147. })
  148. it('v-on shorthand', function () {
  149. el.innerHTML = '<div @click="a++"></div>'
  150. el = el.firstChild
  151. var linker = compile(el, Vue.options)
  152. linker(vm, el)
  153. expect(vm._bindDir.calls.count()).toBe(1)
  154. var args = vm._bindDir.calls.argsFor(0)
  155. expect(args[0].name).toBe('on')
  156. expect(args[0].expression).toBe('a++')
  157. expect(args[0].arg).toBe('click')
  158. expect(args[0].def).toBe(publicDirectives.on)
  159. expect(args[1]).toBe(el)
  160. })
  161. it('text interpolation', function () {
  162. data.b = 'yeah'
  163. el.innerHTML = '{{a}} and {{*b}}'
  164. var def = Vue.options.directives.text
  165. var linker = compile(el, Vue.options)
  166. linker(vm, el)
  167. // expect 1 call because one-time bindings do not generate a directive.
  168. expect(vm._bindDir.calls.count()).toBe(1)
  169. var args = vm._bindDir.calls.argsFor(0)
  170. expect(args[0].name).toBe('text')
  171. expect(args[0].expression).toBe('a')
  172. expect(args[0].def).toBe(def)
  173. // skip the node because it's generated in the linker fn via cloneNode
  174. // expect $eval to be called during onetime
  175. expect(vm.$eval).toHaveBeenCalledWith('b')
  176. // {{a}} is mocked so it's a space.
  177. // but we want to make sure {{*b}} worked.
  178. expect(el.innerHTML).toBe(' and yeah')
  179. })
  180. it('text interpolation, adjacent nodes', function () {
  181. data.b = 'yeah'
  182. el.appendChild(document.createTextNode('{{a'))
  183. el.appendChild(document.createTextNode('}} and {{'))
  184. el.appendChild(document.createTextNode('*b}}'))
  185. var def = Vue.options.directives.text
  186. var linker = compile(el, Vue.options)
  187. linker(vm, el)
  188. // expect 1 call because one-time bindings do not generate a directive.
  189. expect(vm._bindDir.calls.count()).toBe(1)
  190. var args = vm._bindDir.calls.argsFor(0)
  191. expect(args[0].name).toBe('text')
  192. expect(args[0].expression).toBe('a')
  193. expect(args[0].def).toBe(def)
  194. // skip the node because it's generated in the linker fn via cloneNode
  195. // expect $eval to be called during onetime
  196. expect(vm.$eval).toHaveBeenCalledWith('b')
  197. // {{a}} is mocked so it's a space.
  198. // but we want to make sure {{*b}} worked.
  199. expect(el.innerHTML).toBe(' and yeah')
  200. })
  201. it('adjacent text nodes with no interpolation', function () {
  202. el.appendChild(document.createTextNode('a'))
  203. el.appendChild(document.createTextNode('b'))
  204. el.appendChild(document.createTextNode('c'))
  205. var linker = compile(el, Vue.options)
  206. linker(vm, el)
  207. expect(el.innerHTML).toBe('abc')
  208. })
  209. it('inline html', function () {
  210. data.html = '<div>foo</div>'
  211. el.innerHTML = '{{{html}}} {{{*html}}}'
  212. var htmlDef = Vue.options.directives.html
  213. var linker = compile(el, Vue.options)
  214. linker(vm, el)
  215. expect(vm._bindDir.calls.count()).toBe(1)
  216. var htmlArgs = vm._bindDir.calls.argsFor(0)
  217. expect(htmlArgs[0].name).toBe('html')
  218. expect(htmlArgs[0].expression).toBe('html')
  219. expect(htmlArgs[0].def).toBe(htmlDef)
  220. // with placeholder comments & interpolated one-time html
  221. expect(el.innerHTML).toBe('<!--v-html--> <div>foo</div>')
  222. })
  223. it('terminal directives', function () {
  224. el.innerHTML =
  225. '<div v-for="item in items"><p v-a="b"></p></div>' + // v-for
  226. '<div v-pre><p v-a="b"></p></div>' // v-pre
  227. var def = Vue.options.directives.for
  228. var linker = compile(el, Vue.options)
  229. linker(vm, el)
  230. // expect 1 call because terminal should return early and let
  231. // the directive handle the rest.
  232. expect(vm._bindDir.calls.count()).toBe(1)
  233. var args = vm._bindDir.calls.argsFor(0)
  234. expect(args[0].name).toBe('for')
  235. expect(args[0].expression).toBe('item in items')
  236. expect(args[0].def).toBe(def)
  237. expect(args[1]).toBe(el.firstChild)
  238. })
  239. it('custom terminal directives', function () {
  240. var defTerminal = {
  241. terminal: true,
  242. priority: Vue.options.directives.if.priority + 1
  243. }
  244. var options = _.mergeOptions(Vue.options, {
  245. directives: { term: defTerminal }
  246. })
  247. el.innerHTML = '<div v-term:arg1.modifier1.modifier2="foo"></div>'
  248. var linker = compile(el, options)
  249. linker(vm, el)
  250. expect(vm._bindDir.calls.count()).toBe(1)
  251. var args = vm._bindDir.calls.argsFor(0)
  252. expect(args[0].name).toBe('term')
  253. expect(args[0].expression).toBe('foo')
  254. expect(args[0].attr).toBe('v-term:arg1.modifier1.modifier2')
  255. expect(args[0].arg).toBe('arg1')
  256. expect(args[0].modifiers.modifier1).toBe(true)
  257. expect(args[0].modifiers.modifier2).toBe(true)
  258. expect(args[0].def).toBe(defTerminal)
  259. })
  260. it('custom terminal directives priority', function () {
  261. var defTerminal = {
  262. terminal: true,
  263. priority: Vue.options.directives.if.priority + 1
  264. }
  265. var options = _.mergeOptions(Vue.options, {
  266. directives: { term: defTerminal }
  267. })
  268. el.innerHTML = '<div v-term:arg1 v-if="ok"></div>'
  269. var linker = compile(el, options)
  270. linker(vm, el)
  271. expect(vm._bindDir.calls.count()).toBe(1)
  272. var args = vm._bindDir.calls.argsFor(0)
  273. expect(args[0].name).toBe('term')
  274. expect(args[0].expression).toBe('')
  275. expect(args[0].attr).toBe('v-term:arg1')
  276. expect(args[0].arg).toBe('arg1')
  277. expect(args[0].def).toBe(defTerminal)
  278. })
  279. it('custom element components', function () {
  280. var options = _.mergeOptions(Vue.options, {
  281. components: {
  282. 'my-component': {}
  283. }
  284. })
  285. el.innerHTML = '<my-component><div v-a="b"></div></my-component>'
  286. var linker = compile(el, options)
  287. linker(vm, el)
  288. expect(vm._bindDir.calls.count()).toBe(1)
  289. var args = vm._bindDir.calls.argsFor(0)
  290. expect(args[0].name).toBe('component')
  291. expect(args[0].expression).toBe('my-component')
  292. expect(args[0].modifiers.literal).toBe(true)
  293. expect(args[0].def).toBe(internalDirectives.component)
  294. expect(getWarnCount()).toBe(0)
  295. })
  296. it('props', function () {
  297. var bindingModes = Vue.config._propBindingModes
  298. var props = {
  299. testNormal: null,
  300. testLiteral: null,
  301. testBoolean: { type: Boolean },
  302. testTwoWay: null,
  303. twoWayWarn: null,
  304. testOneTime: null,
  305. optimizeLiteral: null,
  306. optimizeLiteralStr: null,
  307. optimizeLiteralNegativeNumber: null,
  308. literalWithFilter: null
  309. }
  310. el.innerHTML = '<div ' +
  311. 'v-bind:test-normal="a" ' +
  312. 'test-literal="1" ' +
  313. 'test-boolean ' +
  314. ':optimize-literal="1" ' +
  315. ':optimize-literal-str="\'true\'"' +
  316. ':optimize-literal-negative-number="-1"' +
  317. ':test-two-way.sync="a" ' +
  318. ':two-way-warn.sync="a + 1" ' +
  319. ':test-one-time.once="a" ' +
  320. ':literal-with-filter="\'FOO\' | lowercase"' +
  321. '></div>'
  322. compiler.compileAndLinkProps(vm, el.firstChild, props)
  323. // check bindDir calls:
  324. // skip literal and one time, but not literal with filter
  325. expect(vm._bindDir.calls.count()).toBe(4)
  326. // literal
  327. expect(vm.testLiteral).toBe('1')
  328. expect(vm.testBoolean).toBe(true)
  329. expect(vm.optimizeLiteral).toBe(1)
  330. expect(vm.optimizeLiteralStr).toBe('true')
  331. expect(vm.optimizeLiteralNegativeNumber).toBe(-1)
  332. // one time
  333. expect(vm.testOneTime).toBe('from parent: a')
  334. // normal
  335. var args = vm._bindDir.calls.argsFor(0)
  336. var prop = args[0].prop
  337. expect(args[0].name).toBe('prop')
  338. expect(prop.path).toBe('testNormal')
  339. expect(prop.parentPath).toBe('a')
  340. expect(prop.mode).toBe(bindingModes.ONE_WAY)
  341. // two way
  342. args = vm._bindDir.calls.argsFor(1)
  343. prop = args[0].prop
  344. expect(args[0].name).toBe('prop')
  345. expect(prop.path).toBe('testTwoWay')
  346. expect(prop.parentPath).toBe('a')
  347. expect(prop.mode).toBe(bindingModes.TWO_WAY)
  348. // two way warn
  349. expect('non-settable parent path').toHaveBeenWarned()
  350. // literal with filter
  351. args = vm._bindDir.calls.argsFor(3)
  352. prop = args[0].prop
  353. expect(args[0].name).toBe('prop')
  354. expect(prop.path).toBe('literalWithFilter')
  355. expect(prop.parentPath).toBe("'FOO'")
  356. expect(prop.filters.length).toBe(1)
  357. expect(prop.mode).toBe(bindingModes.ONE_WAY)
  358. })
  359. it('props on root instance', function () {
  360. // temporarily remove vm.$parent
  361. var context = vm._context
  362. vm._context = null
  363. el.setAttribute('v-bind:a', '"foo"')
  364. el.setAttribute(':b', '[1,2,3]')
  365. compiler.compileAndLinkProps(vm, el, { a: null, b: null })
  366. expect(vm._bindDir.calls.count()).toBe(0)
  367. expect(vm.a).toBe('foo')
  368. expect(vm.b.join(',')).toBe('1,2,3')
  369. // restore parent mock
  370. vm._context = context
  371. })
  372. it('DocumentFragment', function () {
  373. var frag = document.createDocumentFragment()
  374. frag.appendChild(el)
  375. var el2 = document.createElement('div')
  376. frag.appendChild(el2)
  377. el.innerHTML = '{{*a}}'
  378. el2.innerHTML = '{{*b}}'
  379. data.a = 'A'
  380. data.b = 'B'
  381. var linker = compile(frag, Vue.options)
  382. linker(vm, frag)
  383. expect(el.innerHTML).toBe('A')
  384. expect(el2.innerHTML).toBe('B')
  385. })
  386. it('partial compilation', function () {
  387. el.innerHTML = '<div v-bind:test="abc">{{bcd}}<p v-show="ok"></p></div>'
  388. var linker = compile(el, Vue.options, true)
  389. var decompile = linker(vm, el)
  390. expect(vm._directives.length).toBe(3)
  391. decompile()
  392. expect(directiveTeardown.calls.count()).toBe(3)
  393. expect(vm._directives.length).toBe(0)
  394. })
  395. it('skip script tags', function () {
  396. el.innerHTML = '<script type="x/template">{{test}}</script>'
  397. var linker = compile(el, Vue.options)
  398. linker(vm, el)
  399. expect(vm._bindDir.calls.count()).toBe(0)
  400. })
  401. it('should handle container/replacer directives with same name', function () {
  402. var parentSpy = jasmine.createSpy()
  403. var childSpy = jasmine.createSpy()
  404. vm = new Vue({
  405. el: el,
  406. template:
  407. '<test class="a" v-on:click="test(1)"></test>',
  408. methods: {
  409. test: parentSpy
  410. },
  411. components: {
  412. test: {
  413. template: '<div class="b" v-on:click="test(2)"></div>',
  414. replace: true,
  415. methods: {
  416. test: childSpy
  417. }
  418. }
  419. }
  420. })
  421. expect(vm.$el.firstChild.className).toBe('b a')
  422. var e = document.createEvent('HTMLEvents')
  423. e.initEvent('click', true, true)
  424. vm.$el.firstChild.dispatchEvent(e)
  425. expect(parentSpy).toHaveBeenCalledWith(1)
  426. expect(childSpy).toHaveBeenCalledWith(2)
  427. })
  428. it('should teardown props and replacer directives when unlinking', function () {
  429. var vm = new Vue({
  430. el: el,
  431. template: '<test :msg="msg"></test>',
  432. data: {
  433. msg: 'foo'
  434. },
  435. components: {
  436. test: {
  437. props: ['msg'],
  438. template: '<div v-show="true"></div>',
  439. replace: true
  440. }
  441. }
  442. })
  443. var dirs = vm.$children[0]._directives
  444. expect(dirs.length).toBe(2)
  445. vm.$children[0].$destroy()
  446. var i = dirs.length
  447. while (i--) {
  448. expect(dirs[i]._bound).toBe(false)
  449. }
  450. })
  451. it('should remove parent container directives from parent when unlinking', function () {
  452. var vm = new Vue({
  453. el: el,
  454. template:
  455. '<test v-show="ok"></test>',
  456. data: {
  457. ok: true
  458. },
  459. components: {
  460. test: {
  461. template: 'foo'
  462. }
  463. }
  464. })
  465. expect(el.firstChild.style.display).toBe('')
  466. expect(vm._directives.length).toBe(2)
  467. expect(vm.$children.length).toBe(1)
  468. vm.$children[0].$destroy()
  469. expect(vm._directives.length).toBe(1)
  470. expect(vm.$children.length).toBe(0)
  471. })
  472. it('should remove transcluded directives from parent when unlinking (component)', function () {
  473. var vm = new Vue({
  474. el: el,
  475. template:
  476. '<test>{{test}}</test>',
  477. data: {
  478. test: 'parent'
  479. },
  480. components: {
  481. test: {
  482. template: '<slot></slot>'
  483. }
  484. }
  485. })
  486. expect(vm.$el.textContent).toBe('parent')
  487. expect(vm._directives.length).toBe(2)
  488. expect(vm.$children.length).toBe(1)
  489. vm.$children[0].$destroy()
  490. expect(vm._directives.length).toBe(1)
  491. expect(vm.$children.length).toBe(0)
  492. })
  493. it('should remove transcluded directives from parent when unlinking (v-if + component)', function (done) {
  494. var vm = new Vue({
  495. el: el,
  496. template:
  497. '<div v-if="ok">' +
  498. '<test>{{test}}</test>' +
  499. '</div>',
  500. data: {
  501. test: 'parent',
  502. ok: true
  503. },
  504. components: {
  505. test: {
  506. template: '<slot></slot>'
  507. }
  508. }
  509. })
  510. expect(vm.$el.textContent).toBe('parent')
  511. expect(vm._directives.length).toBe(3)
  512. expect(vm.$children.length).toBe(1)
  513. vm.ok = false
  514. _.nextTick(function () {
  515. expect(vm.$el.textContent).toBe('')
  516. expect(vm._directives.length).toBe(1)
  517. expect(vm.$children.length).toBe(0)
  518. done()
  519. })
  520. })
  521. it('element directive', function () {
  522. new Vue({
  523. el: el,
  524. template: '<test>{{a}}</test>',
  525. elementDirectives: {
  526. test: {
  527. bind: function () {
  528. this.el.setAttribute('test', '1')
  529. }
  530. }
  531. }
  532. })
  533. // should be terminal
  534. expect(el.innerHTML).toBe('<test test="1">{{a}}</test>')
  535. })
  536. it('attribute interpolation', function (done) {
  537. var vm = new Vue({
  538. el: el,
  539. template: '<div id="{{a}}" class="b bla-{{c}} d"></div>',
  540. data: {
  541. a: 'aaa',
  542. c: 'ccc'
  543. }
  544. })
  545. expect(el.firstChild.id).toBe('aaa')
  546. expect(el.firstChild.className).toBe('b bla-ccc d')
  547. vm.a = 'aa'
  548. vm.c = 'cc'
  549. _.nextTick(function () {
  550. expect(el.firstChild.id).toBe('aa')
  551. expect(el.firstChild.className).toBe('b bla-cc d')
  552. done()
  553. })
  554. })
  555. it('attribute interpolation: one-time', function (done) {
  556. var vm = new Vue({
  557. el: el,
  558. template: '<div id="{{a}} b {{*c}}"></div>',
  559. data: {
  560. a: 'aaa',
  561. c: 'ccc'
  562. }
  563. })
  564. expect(el.firstChild.id).toBe('aaa b ccc')
  565. vm.a = 'aa'
  566. vm.c = 'cc'
  567. _.nextTick(function () {
  568. expect(el.firstChild.id).toBe('aa b ccc')
  569. done()
  570. })
  571. })
  572. it('attribute interpolation: special cases', function () {
  573. new Vue({
  574. el: el,
  575. template: '<label for="{{a}}" data-test="{{b}}"></label><form accept-charset="{{c}}"></form>',
  576. data: {
  577. a: 'aaa',
  578. b: 'bbb',
  579. c: 'UTF-8'
  580. }
  581. })
  582. expect(el.innerHTML).toBe('<label for="aaa" data-test="bbb"></label><form accept-charset="UTF-8"></form>')
  583. })
  584. it('attribute interpolation: warn invalid', function () {
  585. new Vue({
  586. el: el,
  587. template: '<div v-text="{{a}}"></div>',
  588. data: {
  589. a: '123'
  590. }
  591. })
  592. expect(el.innerHTML).toBe('<div></div>')
  593. expect('attribute interpolation is not allowed in Vue.js directives').toHaveBeenWarned()
  594. })
  595. it('attribute interpolation: warn mixed usage with v-bind', function () {
  596. new Vue({
  597. el: el,
  598. template: '<div class="{{a}}" :class="bcd"></div>',
  599. data: {
  600. a: 'foo'
  601. }
  602. })
  603. expect('Do not mix mustache interpolation and v-bind').toHaveBeenWarned()
  604. })
  605. it('warn directives on fragment instances', function () {
  606. new Vue({
  607. el: el,
  608. template: '<test id="foo" class="ok" :prop="123"></test>',
  609. components: {
  610. test: {
  611. replace: true,
  612. props: ['prop'],
  613. template: '{{prop}}'
  614. }
  615. }
  616. })
  617. expect(getWarnCount()).toBe(1)
  618. expect([
  619. 'Attributes "id", "class" are ignored on component <test>',
  620. 'Attributes "class", "id" are ignored on component <test>'
  621. ]).toHaveBeenWarned()
  622. })
  623. it('should compile component container directives using correct context', function () {
  624. new Vue({
  625. el: el,
  626. directives: {
  627. test: {
  628. bind: function () {
  629. this.el.textContent = 'worked!'
  630. }
  631. }
  632. },
  633. template: '<comp v-test></comp>',
  634. components: { comp: { template: '<div></div>' }}
  635. })
  636. expect(el.textContent).toBe('worked!')
  637. expect(getWarnCount()).toBe(0)
  638. })
  639. // #xxx
  640. it('should compile build-in terminal directive wihtout loop', function (done) {
  641. var vm = new Vue({
  642. el: el,
  643. data: { show: false },
  644. template: '<p v-if:arg1.modifier1="show">hello world</p>'
  645. })
  646. vm.show = true
  647. _.nextTick(function () {
  648. expect(el.textContent).toBe('hello world')
  649. done()
  650. })
  651. })
  652. it('should compile custom terminal directive wihtout loop', function (done) {
  653. var vm = new Vue({
  654. el: el,
  655. data: { show: false },
  656. template: '<p v-if="show" v-inject:modal.modifier1="foo">hello world</p>',
  657. directives: {
  658. inject: {
  659. terminal: true,
  660. priority: Vue.options.directives.if.priority + 1,
  661. bind: function () {
  662. this.anchor = _.createAnchor('v-inject')
  663. _.replace(this.el, this.anchor)
  664. var factory = new FragmentFactory(this.vm, this.el)
  665. this.frag = factory.create(this._host, this._scope, this._frag)
  666. this.frag.before(this.anchor)
  667. },
  668. unbind: function () {
  669. this.frag.remove()
  670. _.replace(this.anchor, this.el)
  671. }
  672. }
  673. }
  674. })
  675. vm.show = true
  676. _.nextTick(function () {
  677. expect(el.textContent).toBe('hello world')
  678. done()
  679. })
  680. })
  681. })