api.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. describe('UNIT: API', function () {
  2. var utils = require('vue/src/utils'),
  3. nextTick = utils.nextTick
  4. describe('config()', function () {
  5. var config = require('vue/src/config')
  6. it('should work when changing prefix', function () {
  7. var testId = 'config-1'
  8. Vue.config({
  9. prefix: 'test'
  10. })
  11. mock(testId, '<span test-text="test"></span>')
  12. new Vue({
  13. el: '#' + testId,
  14. data: { test: testId }
  15. })
  16. assert.strictEqual(document.querySelector('#' + testId + ' span').innerHTML, testId)
  17. })
  18. it('should get', function () {
  19. assert.strictEqual(Vue.config('debug'), false)
  20. })
  21. it('should set', function () {
  22. Vue.config('test', 1)
  23. assert.strictEqual(config.test, 1)
  24. })
  25. after(function () {
  26. Vue.config({
  27. prefix: 'v'
  28. })
  29. })
  30. })
  31. describe('filter()', function () {
  32. var reverse = function (input) {
  33. return input.split('').reverse().join('')
  34. }
  35. it('should create custom filter', function () {
  36. var testId = 'filter-1',
  37. msg = '12345'
  38. Vue.filter('reverse', reverse)
  39. mock(testId, '{{ test | reverse }}')
  40. new Vue({
  41. el: '#' + testId,
  42. data: { test: msg }
  43. })
  44. assert.strictEqual(document.querySelector('#' + testId).innerHTML, '54321')
  45. })
  46. it('should return filter function if only one arg is given', function () {
  47. var f = Vue.filter('reverse')
  48. assert.strictEqual(f, reverse)
  49. })
  50. })
  51. describe('directive()', function () {
  52. var dirTest
  53. it('should create custom directive with set function only', function () {
  54. var testId = 'directive-1',
  55. msg = 'wowow'
  56. Vue.directive('test', function (value) {
  57. this.el.setAttribute(testId, value + '123')
  58. })
  59. mock(testId, '<span v-test="test"></span>')
  60. new Vue({
  61. el: '#' + testId,
  62. data: { test: msg }
  63. })
  64. var el = document.querySelector('#' + testId + ' span')
  65. assert.strictEqual(el.getAttribute(testId), msg + '123')
  66. })
  67. it('should create custom directive with object', function () {
  68. var testId = 'directive-2',
  69. msg = 'wowaaaa?'
  70. dirTest = {
  71. bind: function () {
  72. this.el.setAttribute(testId + 'bind', 'bind')
  73. },
  74. update: function (value) {
  75. this.el.setAttribute(testId + 'update', value + 'update')
  76. },
  77. unbind: function () {
  78. this.el.removeAttribute(testId + 'bind')
  79. }
  80. }
  81. Vue.directive('test2', dirTest)
  82. mock(testId, '<span v-test2="test"></span>')
  83. var vm = new Vue({
  84. el: '#' + testId,
  85. data: { test: msg }
  86. }),
  87. el = document.querySelector('#' + testId + ' span')
  88. assert.strictEqual(el.getAttribute(testId + 'bind'), 'bind', 'should have called bind()')
  89. assert.strictEqual(el.getAttribute(testId + 'update'), msg + 'update', 'should have called update()')
  90. vm.$destroy() // assuming this works
  91. assert.notOk(el.getAttribute(testId + 'bind'), 'should have called unbind()')
  92. })
  93. it('should return directive object/fn if only one arg is given', function () {
  94. var dir = Vue.directive('test2')
  95. assert.strictEqual(dir, dirTest)
  96. })
  97. })
  98. describe('component()', function () {
  99. var testId = 'api-component-test',
  100. testId2 = testId + '2',
  101. opts = {
  102. className: 'hihi',
  103. data: { hi: 'ok' }
  104. },
  105. Test = Vue.extend(opts)
  106. it('should register a Component constructor', function () {
  107. Vue.component(testId, Test)
  108. assert.strictEqual(utils.components[testId], Test)
  109. })
  110. it('should also work with option objects', function () {
  111. Vue.component(testId2, opts)
  112. assert.ok(utils.components[testId2].prototype instanceof Vue)
  113. })
  114. it('should retrieve the VM if has only one arg', function () {
  115. assert.strictEqual(Vue.component(testId), Test)
  116. })
  117. it('should work with v-component', function () {
  118. mock(testId, '<div v-component="' + testId + '">{{hi}}</div>')
  119. var t = new Vue({ el: '#' + testId }),
  120. child = t.$el.querySelector('div')
  121. assert.strictEqual(child.className, 'hihi')
  122. assert.strictEqual(child.textContent, 'ok')
  123. mock(testId2, '<div v-component="' + testId2 + '">{{hi}}</div>')
  124. var t2 = new Vue({ el: '#' + testId2 }),
  125. child2 = t2.$el.querySelector('div')
  126. assert.strictEqual(child2.className, 'hihi')
  127. assert.strictEqual(child2.textContent, 'ok')
  128. })
  129. })
  130. describe('partial()', function () {
  131. var testId = 'api-partial-test',
  132. partial = '<div class="partial-test"><a>{{hi}}</a></div><span>hahaha</span>'
  133. it('should register the partial as a dom fragment', function () {
  134. Vue.partial(testId, partial)
  135. var converted = utils.partials[testId]
  136. assert.ok(converted instanceof window.DocumentFragment)
  137. assert.strictEqual(converted.querySelector('.partial-test a').innerHTML, '{{hi}}')
  138. assert.strictEqual(converted.querySelector('span').innerHTML, 'hahaha')
  139. })
  140. it('should retrieve the partial if has only one arg', function () {
  141. assert.strictEqual(utils.partials[testId], Vue.partial(testId))
  142. })
  143. it('should work with v-partial as a directive', function () {
  144. var testId = 'api-partial-direcitve'
  145. Vue.partial(testId, partial)
  146. mock(testId, '<div class="directive" v-partial="' + testId + '">hello</div>')
  147. var t = new Vue({
  148. el: '#' + testId,
  149. data: { hi: 'hohoho' }
  150. })
  151. assert.strictEqual(t.$el.querySelector('.directive .partial-test a').textContent, 'hohoho')
  152. assert.strictEqual(t.$el.querySelector('.directive span').innerHTML, 'hahaha')
  153. })
  154. it('should work with v-partial as an inline interpolation', function () {
  155. var testId = 'api-partial-inline'
  156. Vue.partial(testId, partial)
  157. mock(testId, '<div class="inline">{{>' + testId + '}}</div>')
  158. var t = new Vue({
  159. el: '#' + testId,
  160. data: { hi: 'hohoho' }
  161. })
  162. assert.strictEqual(t.$el.querySelector('.inline .partial-test a').textContent, 'hohoho')
  163. assert.strictEqual(t.$el.querySelector('.inline span').innerHTML, 'hahaha')
  164. })
  165. })
  166. describe('transition()', function () {
  167. var testId = 'api-trans-test',
  168. transition = {}
  169. it('should register a transition object', function () {
  170. Vue.transition(testId, transition)
  171. assert.strictEqual(utils.transitions[testId], transition)
  172. })
  173. it('should retrieve the transition if has only one arg', function () {
  174. assert.strictEqual(Vue.transition(testId), transition)
  175. })
  176. it('should work with v-transition', function (done) {
  177. var enterCalled = false,
  178. leaveCalled = false
  179. Vue.transition('transition-api-test', {
  180. enter: function (el, done) {
  181. enterCalled = true
  182. done()
  183. },
  184. leave: function (el, done) {
  185. leaveCalled = true
  186. done()
  187. }
  188. })
  189. var t = new Vue({
  190. attributes: {
  191. 'v-show': 'show',
  192. 'v-transition': 'transition-api-test'
  193. },
  194. data: {
  195. show: false
  196. }
  197. })
  198. document.body.appendChild(t.$el)
  199. t.show = true
  200. nextTick(function () {
  201. assert.ok(enterCalled)
  202. assert.strictEqual(t.$el.style.display, '')
  203. t.show = false
  204. nextTick(function () {
  205. assert.ok(leaveCalled)
  206. assert.strictEqual(t.$el.style.display, 'none')
  207. t.$destroy()
  208. done()
  209. })
  210. })
  211. })
  212. })
  213. describe('extend()', function () {
  214. it('should return a subclass of Vue', function () {
  215. var Test = Vue.extend({})
  216. assert.ok(Test.prototype instanceof Vue)
  217. })
  218. it('should allow further extensions', function () {
  219. var Parent = Vue.extend({
  220. data: {
  221. test: 'hi'
  222. }
  223. })
  224. var Child = Parent.extend({
  225. data: {
  226. test2: 'ho',
  227. test3: {
  228. hi: 1
  229. }
  230. }
  231. })
  232. assert.strictEqual(Child.super, Parent)
  233. var child = new Child({
  234. data: {
  235. test3: {
  236. ho: 2
  237. }
  238. }
  239. })
  240. assert.strictEqual(child.test, 'hi')
  241. assert.strictEqual(child.test2, 'ho')
  242. // should overwrite past 1 level deep
  243. assert.strictEqual(child.test3.ho, 2)
  244. assert.notOk(child.test3.hi)
  245. })
  246. describe('Options', function () {
  247. describe('methods', function () {
  248. it('should be mixed to the exteded VM\'s prototype', function () {
  249. var mixins = {
  250. c: function () {},
  251. d: function () {}
  252. }
  253. var Test = Vue.extend({ methods: mixins })
  254. for (var key in mixins) {
  255. assert.strictEqual(Test.prototype[key], mixins[key])
  256. }
  257. })
  258. })
  259. describe('data', function () {
  260. it('should be copied to each instance', function () {
  261. var testData = { a: 1 },
  262. Test = Vue.extend({
  263. data: {
  264. test: testData
  265. }
  266. })
  267. var t1 = new Test(),
  268. t2 = new Test()
  269. assert.ok(t1.hasOwnProperty('test'))
  270. assert.strictEqual(t1.test, testData)
  271. assert.ok(t2.hasOwnProperty('test'))
  272. assert.strictEqual(t2.test, testData)
  273. })
  274. })
  275. describe('lazy', function () {
  276. it('should make text input fields only trigger on change', function () {
  277. var Test = Vue.extend({
  278. template: '<input type="text" v-model="test">',
  279. lazy: true
  280. })
  281. var t = new Test({
  282. data: {
  283. test: 'hi'
  284. }
  285. })
  286. var input = t.$el.querySelector('input')
  287. input.value = 'hohoho'
  288. input.dispatchEvent(mockHTMLEvent('input'))
  289. assert.strictEqual(t.test, 'hi')
  290. input.dispatchEvent(mockHTMLEvent('change'))
  291. assert.strictEqual(t.test, 'hohoho')
  292. })
  293. })
  294. describe('replace', function () {
  295. it('should replace an in DOM node', function () {
  296. var testId = 'replace-test'
  297. mock(testId, '<div>ho</div>')
  298. var old = document.getElementById(testId),
  299. parent = old.parentNode
  300. var Test = Vue.extend({
  301. template: '<p>hi</p>',
  302. replace: true
  303. })
  304. var t = new Test({
  305. el: '#' + testId
  306. })
  307. assert.strictEqual(t.$el.tagName, 'P')
  308. assert.strictEqual(t.$el.textContent, 'hi')
  309. assert.strictEqual(t.$el.parentNode, parent)
  310. var now = document.getElementById(testId)
  311. assert.strictEqual(now, null)
  312. })
  313. it('should replace an off DOM Vue\'s $el', function () {
  314. var Test = Vue.extend({
  315. template: '<p>hi</p>',
  316. replace: true
  317. })
  318. var t = new Test()
  319. assert.strictEqual(t.$el.tagName, 'P')
  320. assert.strictEqual(t.$el.textContent, 'hi')
  321. })
  322. it('should not work if template has more than one child node', function () {
  323. var Test = Vue.extend({
  324. template: '<p>hi</p><p>ho</p>',
  325. replace: true
  326. })
  327. var t = new Test()
  328. assert.notStrictEqual(t.$el.tagName, 'P')
  329. assert.strictEqual(t.$el.innerHTML, '<p>hi</p><p>ho</p>')
  330. })
  331. })
  332. describe('DOM element options', function () {
  333. it('should not accept el as an extension option', function () {
  334. var el = document.createElement('div'),
  335. Test = Vue.extend({ el: el }),
  336. t = new Test()
  337. assert.notStrictEqual(t.$el, el)
  338. })
  339. it('should create el with options: tagName, id, className and attributes', function () {
  340. var Test = Vue.extend({
  341. tagName: 'p',
  342. id: 'extend-test',
  343. className: 'extend',
  344. attributes: {
  345. 'test': 'hi',
  346. 'v-text': 'hoho'
  347. },
  348. data: {
  349. hoho: 'what'
  350. }
  351. })
  352. var t = new Test()
  353. assert.strictEqual(t.$el.nodeName, 'P', 'tagName should match')
  354. assert.strictEqual(t.$el.id, 'extend-test', 'id should match')
  355. assert.strictEqual(t.$el.className, 'extend', 'className should match')
  356. assert.strictEqual(t.$el.getAttribute('test'), 'hi', 'normal attr should work')
  357. assert.strictEqual(t.$el.textContent, 'what', 'directives passed in as attr should work')
  358. })
  359. it('should ignore tagName when el is passed as an instance option', function () {
  360. var el = document.createElement('div'),
  361. Test = Vue.extend({
  362. tagName: 'p',
  363. id: 'extend-test',
  364. className: 'extend',
  365. attributes: {
  366. 'test': 'hi',
  367. 'v-text': 'hoho'
  368. },
  369. data: {
  370. hoho: 'what'
  371. }
  372. }),
  373. t = new Test({
  374. el: el
  375. })
  376. assert.strictEqual(t.$el, el, 'should use instance el')
  377. assert.notStrictEqual(t.$el.nodeName, 'P', 'tagName should NOT match')
  378. assert.strictEqual(t.$el.id, 'extend-test', 'id should match')
  379. assert.strictEqual(t.$el.className, 'extend', 'className should match')
  380. assert.strictEqual(t.$el.getAttribute('test'), 'hi', 'normal attr should work')
  381. assert.strictEqual(t.$el.textContent, 'what', 'directives passed in as attr should work')
  382. })
  383. })
  384. describe('template', function () {
  385. var raw = '<span>{{hello}}</span><a>haha</a>'
  386. it('should take direct string template and work', function () {
  387. var Test = Vue.extend({
  388. tagName: 'p',
  389. template: raw,
  390. data: {
  391. hello: 'Ahaha'
  392. }
  393. }),
  394. vm = new Test(),
  395. text1 = vm.$el.querySelector('span').textContent,
  396. text2 = vm.$el.querySelector('a').textContent
  397. assert.strictEqual(vm.$el.nodeName, 'P')
  398. assert.strictEqual(text1, 'Ahaha')
  399. assert.strictEqual(text2, 'haha')
  400. })
  401. it('should take a #id and work', function () {
  402. var testId = 'template-test',
  403. tpl = document.createElement('script')
  404. tpl.id = testId
  405. tpl.type = 'text/template'
  406. tpl.innerHTML = raw
  407. document.getElementById('test').appendChild(tpl)
  408. var Test = Vue.extend({
  409. template: '#' + testId,
  410. data: { hello: testId }
  411. })
  412. var t = new Test()
  413. assert.strictEqual(t.$el.querySelector('span').textContent, testId)
  414. })
  415. it('should be overwritable', function () {
  416. var Test = Vue.extend({
  417. template: '<div>this should not happen</div>'
  418. })
  419. var t = new Test({
  420. template: raw,
  421. data: {
  422. hello: 'overwritten!'
  423. }
  424. })
  425. assert.strictEqual(t.$el.querySelector('span').textContent, 'overwritten!')
  426. })
  427. })
  428. describe('directives', function () {
  429. it('should allow the VM to use private directives', function (done) {
  430. var Test = Vue.extend({
  431. directives: {
  432. test: function (value) {
  433. this.el.innerHTML = value ? 'YES' : 'NO'
  434. }
  435. }
  436. })
  437. var t = new Test({
  438. attributes: {
  439. 'v-test': 'ok'
  440. },
  441. data: {
  442. ok: true
  443. }
  444. })
  445. assert.strictEqual(t.$el.innerHTML, 'YES')
  446. t.ok = false
  447. nextTick(function () {
  448. assert.strictEqual(t.$el.innerHTML, 'NO')
  449. done()
  450. })
  451. })
  452. })
  453. describe('filters', function () {
  454. it('should allow the VM to use private filters', function () {
  455. var Test = Vue.extend({
  456. filters: {
  457. test: function (value) {
  458. return value + '12345'
  459. }
  460. }
  461. })
  462. var t = new Test({
  463. template: '{{hi | test}}',
  464. data: {
  465. hi: 'hohoho'
  466. }
  467. })
  468. assert.strictEqual(t.$el.textContent, 'hohoho12345')
  469. })
  470. })
  471. describe('components', function () {
  472. it('should allow the VM to use private child VMs', function () {
  473. var Child = Vue.extend({
  474. data: {
  475. name: 'child'
  476. }
  477. })
  478. var Parent = Vue.extend({
  479. template: '<p>{{name}}</p><div v-component="child">{{name}}</div>',
  480. data: {
  481. name: 'dad'
  482. },
  483. components: {
  484. child: Child
  485. }
  486. })
  487. var p = new Parent()
  488. assert.strictEqual(p.$el.querySelector('p').textContent, 'dad')
  489. assert.strictEqual(p.$el.querySelector('div').textContent, 'child')
  490. })
  491. it('should work with plain option object', function () {
  492. var Parent = Vue.extend({
  493. template: '<p>{{name}}</p><div v-component="child">{{name}}</div>',
  494. data: {
  495. name: 'dad'
  496. },
  497. components: {
  498. child: {
  499. data: {
  500. name: 'child'
  501. }
  502. }
  503. }
  504. })
  505. var p = new Parent()
  506. assert.strictEqual(p.$el.querySelector('p').textContent, 'dad')
  507. assert.strictEqual(p.$el.querySelector('div').textContent, 'child')
  508. })
  509. })
  510. describe('partials', function () {
  511. it('should allow the VM to use private partials', function () {
  512. var Test = Vue.extend({
  513. attributes: {
  514. 'v-partial': 'test'
  515. },
  516. partials: {
  517. test: '<a>{{a}}</a><p>{{b}}</p>'
  518. },
  519. data: {
  520. a: 'hi',
  521. b: 'ho'
  522. }
  523. })
  524. var t = new Test()
  525. assert.strictEqual(t.$el.querySelector('a').textContent, 'hi')
  526. assert.strictEqual(t.$el.querySelector('p').textContent, 'ho')
  527. })
  528. })
  529. describe('transitions', function () {
  530. it('should get called during transitions', function (done) {
  531. var enterCalled = false,
  532. leaveCalled = false
  533. var t = new Vue({
  534. attributes: {
  535. 'v-show': 'show',
  536. 'v-transition': 'test'
  537. },
  538. transitions: {
  539. test: {
  540. enter: function (el, done) {
  541. enterCalled = true
  542. done()
  543. },
  544. leave: function (el, done) {
  545. leaveCalled = true
  546. done()
  547. }
  548. }
  549. },
  550. data: {
  551. show: false
  552. }
  553. })
  554. document.body.appendChild(t.$el)
  555. t.show = true
  556. nextTick(function () {
  557. assert.ok(enterCalled)
  558. assert.strictEqual(t.$el.style.display, '')
  559. t.show = false
  560. nextTick(function () {
  561. assert.ok(leaveCalled)
  562. assert.strictEqual(t.$el.style.display, 'none')
  563. t.$destroy()
  564. done()
  565. })
  566. })
  567. })
  568. })
  569. describe('hooks', function () {
  570. describe('beforeCompile / created', function () {
  571. it('should be called before compile', function () {
  572. var called = false,
  573. Test = Vue.extend({ beforeCompile: function (options) {
  574. assert.ok(options.ok)
  575. called = true
  576. }}),
  577. Test2 = Vue.extend({ created: function (options) {
  578. assert.ok(options.ok)
  579. called = true
  580. }})
  581. new Test({ ok: true })
  582. assert.ok(called)
  583. called = false
  584. new Test2({ ok: true })
  585. assert.ok(called)
  586. })
  587. })
  588. describe('afterCompile / ready', function () {
  589. it('should be called after compile with options', function () {
  590. var called = false,
  591. hook = function (options) {
  592. assert.ok(options.ok)
  593. assert.notOk(this.$compiler.init)
  594. called = true
  595. },
  596. Test = Vue.extend({ afterCompile: hook }),
  597. Test2 = Vue.extend({ ready: hook })
  598. new Test({ ok: true })
  599. assert.ok(called)
  600. called = false
  601. new Test2({ ok: true })
  602. assert.ok(called)
  603. })
  604. })
  605. describe('beforeDestroy', function () {
  606. it('should be called before a vm is destroyed', function () {
  607. var called = false
  608. var Test = Vue.extend({
  609. beforeDestroy: function () {
  610. called = true
  611. }
  612. })
  613. var test = new Test()
  614. test.$destroy()
  615. assert.ok(called)
  616. })
  617. })
  618. describe('afterDestroy', function () {
  619. it('should be called after a vm is destroyed', function () {
  620. var called = false,
  621. Test = Vue.extend({
  622. afterDestroy: function () {
  623. assert.notOk(this.$el.parentNode)
  624. called = true
  625. }
  626. })
  627. var test = new Test()
  628. document.body.appendChild(test.$el)
  629. test.$destroy()
  630. assert.ok(called)
  631. })
  632. })
  633. describe('Hook inheritance', function () {
  634. it('should merge hooks with parent Class', function () {
  635. var parentCreated = false,
  636. childCreated = false
  637. var Parent = Vue.extend({
  638. created: function () {
  639. parentCreated = true
  640. }
  641. })
  642. var Child = Parent.extend({
  643. created: function () {
  644. childCreated = true
  645. }
  646. })
  647. new Child()
  648. assert.ok(parentCreated)
  649. assert.ok(childCreated)
  650. })
  651. })
  652. })
  653. })
  654. })
  655. })