directives.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. describe('UNIT: Directives', function () {
  2. var nextTick = require('vue/src/utils').nextTick,
  3. VM = require('vue/src/viewmodel')
  4. describe('attr', function () {
  5. var dir = mockDirective('attr', 'input'),
  6. el = dir.el
  7. it('should set a truthy attribute value', function () {
  8. var value = 'Arrrrrr!'
  9. dir.arg = 'value'
  10. dir.update(value)
  11. assert.strictEqual(el.getAttribute('value'), value)
  12. })
  13. it('should set attribute value to `0`', function () {
  14. dir.arg = 'value'
  15. dir.update(0)
  16. assert.strictEqual(el.getAttribute('value'), '0')
  17. })
  18. it('should remove an attribute if value is `false`', function () {
  19. dir.arg = 'disabled'
  20. el.setAttribute('disabled', 'disabled')
  21. dir.update(false)
  22. assert.strictEqual(el.getAttribute('disabled'), null)
  23. })
  24. it('should remove an attribute if value is `null`', function () {
  25. dir.arg = 'disabled'
  26. el.setAttribute('disabled', 'disabled')
  27. dir.update(null)
  28. assert.strictEqual(el.getAttribute('disabled'), null)
  29. })
  30. it('should remove an attribute if value is `undefined`', function () {
  31. dir.arg = 'disabled'
  32. el.setAttribute('disabled', 'disabled')
  33. dir.update(undefined)
  34. assert.strictEqual(el.getAttribute('disabled'), null)
  35. })
  36. })
  37. describe('text', function () {
  38. var dir = mockDirective('text')
  39. it('should work with a string', function () {
  40. dir.update('hallo')
  41. assert.strictEqual(dir.el.textContent, 'hallo')
  42. })
  43. it('should work with a number', function () {
  44. dir.update(12345)
  45. assert.strictEqual(dir.el.textContent, '12345')
  46. })
  47. it('should work with booleans', function () {
  48. dir.update(true)
  49. assert.strictEqual(dir.el.textContent, 'true')
  50. })
  51. it('should work with objects', function () {
  52. dir.update({foo:"bar"})
  53. assert.strictEqual(dir.el.textContent, '{"foo":"bar"}')
  54. })
  55. it('should be empty with other stuff', function () {
  56. dir.update(null)
  57. assert.strictEqual(dir.el.textContent, '')
  58. dir.update(undefined)
  59. assert.strictEqual(dir.el.textContent, '')
  60. dir.update(function () {})
  61. assert.strictEqual(dir.el.textContent, '')
  62. })
  63. })
  64. describe('html', function () {
  65. var dir = mockDirective('html')
  66. it('should work with a string', function () {
  67. dir.update('hi!!!')
  68. assert.strictEqual(dir.el.innerHTML, 'hi!!!')
  69. dir.update('<span>haha</span><a>lol</a>')
  70. assert.strictEqual(dir.el.querySelector('span').textContent, 'haha')
  71. })
  72. it('should work with a number', function () {
  73. dir.update(12345)
  74. assert.strictEqual(dir.el.innerHTML, '12345')
  75. })
  76. it('should work with booleans', function () {
  77. dir.update(true)
  78. assert.strictEqual(dir.el.textContent, 'true')
  79. })
  80. it('should work with objects', function () {
  81. dir.update({foo:"bar"})
  82. assert.strictEqual(dir.el.textContent, '{"foo":"bar"}')
  83. })
  84. it('should be empty with other stuff', function () {
  85. dir.update(null)
  86. assert.strictEqual(dir.el.innerHTML, '')
  87. dir.update(undefined)
  88. assert.strictEqual(dir.el.innerHTML, '')
  89. dir.update(function () {})
  90. assert.strictEqual(dir.el.innerHTML, '')
  91. })
  92. it('should swap html if el is a comment placeholder', function () {
  93. var dir = mockDirective('html'),
  94. comment = document.createComment('hi'),
  95. parent = dir.el
  96. parent.innerHTML = 'what!'
  97. parent.appendChild(comment)
  98. dir.el = comment
  99. dir.bind()
  100. assert.ok(dir.holder)
  101. assert.ok(dir.nodes)
  102. var pre = 'what!',
  103. after = '<!--hi-->',
  104. h1 = '<span>hello</span><span>world</span>',
  105. h2 = '<a>whats</a><a>up</a>'
  106. dir.update(h1)
  107. assert.strictEqual(parent.innerHTML, pre + h1 + after)
  108. dir.update(h2)
  109. assert.strictEqual(parent.innerHTML, pre + h2 + after)
  110. })
  111. })
  112. describe('show', function () {
  113. var dir = mockDirective('show')
  114. it('should be default value when value is truthy', function () {
  115. dir.update(1)
  116. assert.strictEqual(dir.el.style.display, '')
  117. dir.update('hi!')
  118. assert.strictEqual(dir.el.style.display, '')
  119. dir.update(true)
  120. assert.strictEqual(dir.el.style.display, '')
  121. dir.update({})
  122. assert.strictEqual(dir.el.style.display, '')
  123. dir.update(function () {})
  124. assert.strictEqual(dir.el.style.display, '')
  125. })
  126. it('should be none when value is falsy', function () {
  127. dir.update(0)
  128. assert.strictEqual(dir.el.style.display, 'none')
  129. dir.update('')
  130. assert.strictEqual(dir.el.style.display, 'none')
  131. dir.update(false)
  132. assert.strictEqual(dir.el.style.display, 'none')
  133. dir.update(null)
  134. assert.strictEqual(dir.el.style.display, 'none')
  135. dir.update(undefined)
  136. assert.strictEqual(dir.el.style.display, 'none')
  137. })
  138. })
  139. describe('class', function () {
  140. function contains (el, cls) {
  141. var cur = ' ' + el.className + ' '
  142. return cur.indexOf(' ' + cls + ' ') > -1
  143. }
  144. it('should set class to the value if it has no arg', function () {
  145. var dir = mockDirective('class')
  146. dir.update('test')
  147. assert.ok(contains(dir.el, 'test'))
  148. dir.update('hoho')
  149. assert.ok(!contains(dir.el, 'test'))
  150. assert.ok(contains(dir.el, 'hoho'))
  151. })
  152. it('should add/remove class based on truthy/falsy if it has an arg', function () {
  153. var dir = mockDirective('class')
  154. dir.arg = 'test'
  155. dir.update(true)
  156. assert.ok(contains(dir.el, 'test'))
  157. dir.update(false)
  158. assert.ok(!contains(dir.el, 'test'))
  159. })
  160. })
  161. describe('model', function () {
  162. describe('input[checkbox]', function () {
  163. var dir = mockDirective('model', 'input', 'checkbox')
  164. dir.bind()
  165. before(function () {
  166. document.body.appendChild(dir.el)
  167. })
  168. it('should set checked on update()', function () {
  169. dir.update(true)
  170. assert.ok(dir.el.checked)
  171. dir.update(false)
  172. assert.ok(!dir.el.checked)
  173. })
  174. it('should trigger vm.$set when clicked', function () {
  175. var triggered = false
  176. dir.key = 'foo'
  177. dir.ownerVM = { $set: function (key, val) {
  178. assert.strictEqual(key, 'foo')
  179. assert.strictEqual(val, true)
  180. triggered = true
  181. }}
  182. dir.el.dispatchEvent(mockMouseEvent('click'))
  183. assert.ok(triggered)
  184. })
  185. it('should remove event listener with unbind()', function () {
  186. var removed = true
  187. dir.ownerVM = {
  188. $set: function () {
  189. removed = false
  190. }
  191. }
  192. dir.unbind()
  193. dir.el.dispatchEvent(mockMouseEvent('click'))
  194. assert.ok(removed)
  195. })
  196. after(function () {
  197. document.body.removeChild(dir.el)
  198. })
  199. })
  200. describe('input[radio]', function () {
  201. var dir1 = mockDirective('model', 'input', 'radio'),
  202. dir2 = mockDirective('model', 'input', 'radio')
  203. dir1.el.name = 'input-radio'
  204. dir2.el.name = 'input-radio'
  205. dir1.el.value = '12345'
  206. dir2.el.value = '54321'
  207. dir1.bind()
  208. dir2.bind()
  209. before(function () {
  210. document.body.appendChild(dir1.el)
  211. document.body.appendChild(dir2.el)
  212. })
  213. it('should set el.checked on update()', function () {
  214. assert.notOk(dir1.el.checked)
  215. dir1.update(12345)
  216. assert.ok(dir1.el.checked)
  217. })
  218. it('should trigger vm.$set when clicked', function () {
  219. var triggered = false
  220. dir2.key = 'radio'
  221. dir2.ownerVM = { $set: function (key, val) {
  222. triggered = true
  223. assert.strictEqual(key, 'radio')
  224. assert.strictEqual(val, dir2.el.value)
  225. }}
  226. dir2.el.dispatchEvent(mockMouseEvent('click'))
  227. assert.ok(triggered)
  228. assert.ok(dir2.el.checked)
  229. assert.notOk(dir1.el.checked)
  230. })
  231. it('should remove listeners on unbind()', function () {
  232. var removed = true
  233. dir1.ownerVM = { $set: function () {
  234. removed = false
  235. }}
  236. dir1.unbind()
  237. dir1.el.dispatchEvent(mockMouseEvent('click'))
  238. assert.ok(removed)
  239. })
  240. after(function () {
  241. document.body.removeChild(dir1.el)
  242. document.body.removeChild(dir2.el)
  243. })
  244. })
  245. describe('select', function () {
  246. var dir = mockDirective('model', 'select'),
  247. o1 = document.createElement('option'),
  248. o2 = document.createElement('option')
  249. o1.value = 0
  250. o2.value = 1
  251. dir.el.appendChild(o1)
  252. dir.el.appendChild(o2)
  253. dir.bind()
  254. before(function () {
  255. document.body.appendChild(dir.el)
  256. })
  257. it('should set value on update()', function () {
  258. dir.update(0)
  259. assert.strictEqual(dir.el.value, '0')
  260. })
  261. it('should trigger vm.$set when value is changed', function () {
  262. var triggered = false
  263. dir.key = 'select'
  264. dir.ownerVM = { $set: function (key, val) {
  265. triggered = true
  266. assert.strictEqual(key, 'select')
  267. assert.equal(val, 1)
  268. }}
  269. dir.el.options.selectedIndex = 1
  270. dir.el.dispatchEvent(mockHTMLEvent('change'))
  271. assert.ok(triggered)
  272. })
  273. it('should remove listener on unbind()', function () {
  274. var removed = true
  275. dir.ownerVM = { $set: function () {
  276. removed = false
  277. }}
  278. dir.unbind()
  279. dir.el.dispatchEvent(mockHTMLEvent('change'))
  280. assert.ok(removed)
  281. })
  282. after(function () {
  283. document.body.removeChild(dir.el)
  284. })
  285. })
  286. describe('input[text] and others', function () {
  287. var dir = mockDirective('model', 'input', 'text')
  288. dir.bind()
  289. before(function () {
  290. document.body.appendChild(dir.el)
  291. })
  292. it('should set the value on update()', function () {
  293. dir.update('foobar')
  294. assert.strictEqual(dir.el.value, 'foobar')
  295. })
  296. // `lazy` option is tested in the API suite
  297. it('should trigger vm.$set when value is changed via input', function () {
  298. var triggered = false
  299. dir.key = 'foo'
  300. dir.ownerVM = { $set: function (key, val) {
  301. assert.ok(dir.lock, 'the directive should be locked if it has no filters')
  302. assert.strictEqual(key, 'foo')
  303. assert.strictEqual(val, 'bar')
  304. triggered = true
  305. }}
  306. dir.el.value = 'bar'
  307. dir.el.dispatchEvent(mockHTMLEvent('input'))
  308. assert.ok(triggered)
  309. })
  310. it('should remove event listener with unbind()', function () {
  311. var removed = true
  312. dir.ownerVM = {
  313. $set: function () {
  314. removed = false
  315. }
  316. }
  317. dir.unbind()
  318. dir.el.dispatchEvent(mockHTMLEvent('input'))
  319. assert.ok(removed)
  320. })
  321. it('should not lock during vm.$set if it has filters', function (done) {
  322. var triggered = false
  323. var dir = mockDirective('model', 'input', 'text')
  324. dir.filters = []
  325. dir.bind()
  326. dir.ownerVM = {$set:function () {
  327. assert.notOk(dir.lock)
  328. triggered = true
  329. }}
  330. dir.el.value = 'foo'
  331. document.body.appendChild(dir.el)
  332. dir.el.dispatchEvent(mockHTMLEvent('input'))
  333. // timeout becuase the update is async
  334. setTimeout(function () {
  335. assert.ok(triggered)
  336. document.body.removeChild(dir.el)
  337. done()
  338. }, 1)
  339. })
  340. after(function () {
  341. document.body.removeChild(dir.el)
  342. })
  343. })
  344. })
  345. describe('if', function () {
  346. it('should remain detached if it was detached during bind() and never attached', function () {
  347. var dir = mockDirective('if')
  348. dir.bind()
  349. dir.update(true)
  350. assert.notOk(dir.el.parentNode)
  351. dir.update(false)
  352. assert.notOk(dir.el.parentNode)
  353. })
  354. it('should remove el and insert ref when value is falsy', function () {
  355. var dir = mockDirective('if'),
  356. parent = document.createElement('div')
  357. parent.appendChild(dir.el)
  358. dir.bind()
  359. dir.update(false)
  360. assert.notOk(dir.el.parentNode)
  361. assert.notOk(parent.contains(dir.el))
  362. // phantomJS weird bug:
  363. // Node.contains() returns false when argument is a comment node.
  364. assert.strictEqual(dir.ref.parentNode, parent)
  365. })
  366. it('should append el and remove ref when value is truthy', function () {
  367. var dir = mockDirective('if'),
  368. parent = document.createElement('div')
  369. parent.appendChild(dir.el)
  370. dir.bind()
  371. dir.update(false)
  372. dir.update(true)
  373. assert.strictEqual(dir.el.parentNode, parent)
  374. assert.ok(parent.contains(dir.el))
  375. assert.notOk(parent.contains(dir.ref))
  376. })
  377. it('should work even if it was detached during bind()', function () {
  378. var dir = mockDirective('if')
  379. dir.bind()
  380. var parent = document.createElement('div')
  381. parent.appendChild(dir.el)
  382. dir.update(false)
  383. assert.strictEqual(dir.parent, parent)
  384. assert.notOk(dir.el.parentNode)
  385. assert.notOk(parent.contains(dir.el))
  386. assert.strictEqual(dir.ref.parentNode, parent)
  387. dir.update(true)
  388. assert.strictEqual(dir.el.parentNode, parent)
  389. assert.ok(parent.contains(dir.el))
  390. assert.notOk(parent.contains(dir.ref))
  391. })
  392. })
  393. describe('on (non-delegated only)', function () {
  394. var dir = mockDirective('on')
  395. dir.arg = 'click'
  396. before(function () {
  397. document.body.appendChild(dir.el)
  398. })
  399. it('should set the handler to be triggered by arg through update()', function () {
  400. var triggered = false
  401. dir.update(function () {
  402. triggered = true
  403. })
  404. dir.el.dispatchEvent(mockMouseEvent('click'))
  405. assert.ok(triggered)
  406. })
  407. it('should wrap the handler to supply expected args', function () {
  408. var vm = dir.binding.compiler.vm, // owner VM
  409. e = mockMouseEvent('click'), // original event
  410. triggered = false
  411. dir.update(function (ev) {
  412. assert.strictEqual(this, vm, 'handler should be called on owner VM')
  413. assert.strictEqual(ev, e, 'event should be passed in')
  414. assert.strictEqual(ev.vm, dir.vm)
  415. triggered = true
  416. })
  417. dir.el.dispatchEvent(e)
  418. assert.ok(triggered)
  419. })
  420. it('should remove previous handler when update() a new handler', function () {
  421. var triggered1 = false,
  422. triggered2 = false
  423. dir.update(function () {
  424. triggered1 = true
  425. })
  426. dir.update(function () {
  427. triggered2 = true
  428. })
  429. dir.el.dispatchEvent(mockMouseEvent('click'))
  430. assert.notOk(triggered1)
  431. assert.ok(triggered2)
  432. })
  433. it('should remove the handler in unbind()', function () {
  434. var triggered = false
  435. dir.update(function () {
  436. triggered = true
  437. })
  438. dir.unbind()
  439. dir.el.dispatchEvent(mockMouseEvent('click'))
  440. assert.notOk(triggered)
  441. assert.strictEqual(dir.handler, null, 'should remove reference to handler')
  442. assert.strictEqual(dir.el.vue_viewmodel, null, 'should remove reference to VM on the element')
  443. })
  444. it('should not use delegation if the event is blur or focus', function () {
  445. var dir = mockDirective('on', 'input'),
  446. triggerCount = 0,
  447. handler = function () {
  448. triggerCount++
  449. }
  450. document.body.appendChild(dir.el)
  451. dir.arg = 'focus'
  452. dir.update(handler)
  453. dir.el.dispatchEvent(mockHTMLEvent('focus'))
  454. assert.strictEqual(triggerCount, 1)
  455. dir.arg = 'blur'
  456. dir.update(handler)
  457. dir.el.dispatchEvent(mockHTMLEvent('blur'))
  458. assert.strictEqual(triggerCount, 2)
  459. document.body.removeChild(dir.el)
  460. })
  461. after(function () {
  462. document.body.removeChild(dir.el)
  463. })
  464. })
  465. describe('pre', function () {
  466. it('should skip compilation', function () {
  467. var testId = 'pre-test'
  468. mock(testId, '<span v-pre><strong>{{lol}}</strong><a v-text="hi"></a></span>')
  469. var t = new Vue({
  470. el: '#' + testId,
  471. data: {
  472. lol: 'heyhey',
  473. hi: 'hohoho'
  474. }
  475. })
  476. assert.strictEqual(t.$el.querySelector('strong').textContent, '{{lol}}')
  477. assert.strictEqual(t.$el.querySelector('a').textContent, '')
  478. assert.ok(t.$el.querySelector('a').hasAttribute('v-text'))
  479. })
  480. })
  481. describe('component', function () {
  482. it('should create a child viewmodel with given constructor', function () {
  483. var testId = 'component-test'
  484. mock(testId, '<div v-component="' + testId + '"></div>')
  485. var t = new Vue({
  486. el: '#' + testId,
  487. data: {
  488. msg: '123'
  489. },
  490. components: {
  491. 'component-test': {
  492. template: '<span>{{msg}}</span>'
  493. }
  494. }
  495. })
  496. assert.strictEqual(t.$el.querySelector('span').textContent, '123')
  497. })
  498. })
  499. describe('with', function () {
  500. it('should create a child viewmodel with given data', function () {
  501. var testId = 'with-test'
  502. mock(testId, '<span v-with="test">{{msg}}</span>')
  503. var t = new Vue({
  504. el: '#' + testId,
  505. data: {
  506. test: {
  507. msg: testId
  508. }
  509. }
  510. })
  511. assert.strictEqual(t.$el.querySelector('span').textContent, testId)
  512. })
  513. })
  514. describe('ref', function () {
  515. it('should register a VM isntance on its parent\'s $', function () {
  516. var called = false
  517. var Child = Vue.extend({
  518. methods: {
  519. test: function () {
  520. called = true
  521. }
  522. }
  523. })
  524. var t = new Vue({
  525. template: '<div v-component="child" v-ref="hihi"></div>',
  526. components: {
  527. child: Child
  528. }
  529. })
  530. assert.ok(t.$.hihi instanceof Child)
  531. t.$.hihi.test()
  532. assert.ok(called)
  533. t.$.hihi.$destroy()
  534. assert.notOk('hihi' in t.$)
  535. })
  536. })
  537. // More detailed testing for v-repeat can be found in functional tests.
  538. // this is mainly for code coverage
  539. describe('repeat', function () {
  540. it('should work', function (done) {
  541. var handlerCalled = false
  542. var v = new Vue({
  543. template: '<span v-repeat="items" v-on="click:check">{{title}}</span>',
  544. data: {
  545. items: [
  546. {title: 1},
  547. {title: 2}
  548. ]
  549. },
  550. methods: {
  551. check: function (e) {
  552. assert.ok(e.targetVM instanceof VM)
  553. assert.strictEqual(this, v)
  554. handlerCalled = true
  555. }
  556. }
  557. })
  558. nextTick(function () {
  559. assert.equal(v.$el.innerHTML, '<span>1</span><span>2</span><!--v-repeat-items-->')
  560. v.items.push({title:3})
  561. v.items.pop()
  562. v.items.unshift({title:0})
  563. v.items.shift()
  564. v.items.splice(0, 1, {title:-1})
  565. v.items.sort(function (a, b) {
  566. return a.title > b.title
  567. })
  568. v.items.reverse()
  569. nextTick(function () {
  570. assert.equal(v.$el.innerHTML, '<span>2</span><span>-1</span><!--v-repeat-items-->')
  571. testHandler()
  572. })
  573. })
  574. function testHandler () {
  575. document.getElementById('test').appendChild(v.$el)
  576. var span = v.$el.querySelector('span'),
  577. e = mockMouseEvent('click')
  578. span.dispatchEvent(e)
  579. nextTick(function () {
  580. assert.ok(handlerCalled)
  581. done()
  582. })
  583. }
  584. })
  585. it('should work with primitive values', function () {
  586. var v = new Vue({
  587. template: '<span v-repeat="tags" v-ref="tags">{{$value}}</span>',
  588. data: {
  589. tags: ['a', 'b', 'c']
  590. }
  591. })
  592. assert.strictEqual(v.$el.textContent, 'abc')
  593. v.$.tags[0].$value = 'd'
  594. assert.strictEqual(v.tags[0], 'd')
  595. })
  596. it('should diff and reuse existing VMs when reseting arrays', function (done) {
  597. var v = new Vue({
  598. template: '<span v-repeat="tags" v-ref="tags">{{$value}}</span>',
  599. data: {
  600. tags: ['a', 'b', 'c']
  601. }
  602. })
  603. var oldVMs = v.$.tags
  604. v.tags = v.tags.slice()
  605. nextTick(function () {
  606. assert.deepEqual(oldVMs, v.$.tags)
  607. done()
  608. })
  609. })
  610. it('should also work on objects', function (done) {
  611. var v = new Vue({
  612. template: '<span v-repeat="obj">{{$key}} {{msg}}</span>',
  613. data: {
  614. obj: {
  615. a: {
  616. msg: 'hi!'
  617. },
  618. b: {
  619. msg: 'ha!'
  620. }
  621. }
  622. }
  623. })
  624. assert.strictEqual(v.$el.textContent, 'a hi!b ha!')
  625. v.obj.a.msg = 'ho!'
  626. nextTick(function () {
  627. assert.strictEqual(v.$el.textContent, 'a ho!b ha!')
  628. testAddKey()
  629. })
  630. function testAddKey () {
  631. v.obj.$repeater.push({ $key: 'c', msg: 'he!' })
  632. nextTick(function () {
  633. assert.strictEqual(v.$el.textContent, 'a ho!b ha!c he!')
  634. assert.strictEqual(v.obj.c.msg, 'he!')
  635. testRemoveKey()
  636. })
  637. }
  638. function testRemoveKey () {
  639. v.obj.$repeater.shift()
  640. nextTick(function () {
  641. assert.strictEqual(v.$el.textContent, 'b ha!c he!')
  642. assert.strictEqual(v.obj.a, undefined)
  643. testSwap()
  644. })
  645. }
  646. function testSwap () {
  647. v.obj.b = { msg: 'hehe' }
  648. nextTick(function () {
  649. assert.strictEqual(v.$el.textContent, 'b hehec he!')
  650. testRootSwap()
  651. })
  652. }
  653. function testRootSwap () {
  654. v.obj = { b: { msg: 'wa'}, c: {msg: 'wo'} }
  655. nextTick(function () {
  656. assert.strictEqual(v.$el.textContent, 'b wac wo')
  657. done()
  658. })
  659. }
  660. })
  661. })
  662. describe('style', function () {
  663. it('should apply a normal style', function () {
  664. var d = mockDirective('style')
  665. d.arg = 'text-align'
  666. d.bind()
  667. assert.strictEqual(d.prop, 'textAlign')
  668. d.update('center')
  669. assert.strictEqual(d.el.style.textAlign, 'center')
  670. })
  671. it('should apply prefixed style', function () {
  672. var d = mockDirective('style')
  673. d.arg = '-webkit-transform'
  674. d.bind()
  675. assert.strictEqual(d.prop, 'webkitTransform')
  676. d.update('scale(2)')
  677. assert.strictEqual(d.el.style.webkitTransform, 'scale(2)')
  678. })
  679. it('should auto prefix styles', function () {
  680. var d = mockDirective('style')
  681. d.arg = '$transform'
  682. d.bind()
  683. assert.ok(d.prefixed)
  684. assert.strictEqual(d.prop, 'transform')
  685. var val = 'scale(2)'
  686. d.update(val)
  687. assert.strictEqual(d.el.style.transform, val)
  688. assert.strictEqual(d.el.style.webkitTransform, val)
  689. assert.strictEqual(d.el.style.mozTransform, val)
  690. assert.strictEqual(d.el.style.msTransform, val)
  691. })
  692. it('should set cssText if no arg', function () {
  693. var d = mockDirective('style')
  694. d.bind()
  695. var val = 'color:#fff'
  696. d.update(val)
  697. assert.strictEqual(d.el.style.color, 'rgb(255, 255, 255)')
  698. })
  699. })
  700. describe('cloak', function () {
  701. it('should remove itself after the instance is ready', function () {
  702. // it doesn't make sense to test with a mock for this one, so...
  703. var v = new Vue({
  704. template: '<div v-cloak></div>',
  705. replace: true,
  706. ready: function () {
  707. // this hook is attached before the v-cloak hook
  708. // so it should still have the attribute
  709. assert.ok(this.$el.hasAttribute('v-cloak'))
  710. }
  711. })
  712. assert.notOk(v.$el.hasAttribute('v-cloak'))
  713. })
  714. })
  715. describe('data', function () {
  716. it('should set data on the child VM', function () {
  717. var v = new Vue({
  718. template: '<div v-component="test" v-ref="test" v-data="a:1,b:hi"></div>',
  719. components: {
  720. test: Vue
  721. }
  722. })
  723. assert.strictEqual(v.$.test.a, 1)
  724. assert.strictEqual(v.$.test.b, 'hi')
  725. })
  726. })
  727. })
  728. function mockDirective (dirName, tag, type) {
  729. var dir = Vue.directive(dirName),
  730. ret = {
  731. binding: { compiler: { vm: {} } },
  732. compiler: { vm: {}, options: {}, execHook: function () {}},
  733. el: document.createElement(tag || 'div')
  734. }
  735. if (typeof dir === 'function') {
  736. ret.update = dir
  737. } else {
  738. for (var key in dir) {
  739. ret[key] = dir[key]
  740. }
  741. }
  742. if (tag === 'input') ret.el.type = type || 'text'
  743. return ret
  744. }