api.js 30 KB

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