component_spec.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. var _ = require('../../../../src/util')
  2. var Vue = require('../../../../src/vue')
  3. if (_.inBrowser) {
  4. describe('v-component', function () {
  5. var el
  6. beforeEach(function () {
  7. el = document.createElement('div')
  8. document.body.appendChild(el)
  9. spyOn(_, 'warn')
  10. })
  11. afterEach(function () {
  12. document.body.removeChild(el)
  13. })
  14. it('static', function () {
  15. var vm = new Vue({
  16. el: el,
  17. template: '<test></test>',
  18. components: {
  19. test: {
  20. data: function () {
  21. return { a: 123 }
  22. },
  23. template: '{{a}}'
  24. }
  25. }
  26. })
  27. expect(el.innerHTML).toBe('<test>123</test>')
  28. })
  29. it('replace', function () {
  30. var vm = new Vue({
  31. el: el,
  32. template: '<test></test>',
  33. components: {
  34. test: {
  35. replace: true,
  36. data: function () {
  37. return { a: 123 }
  38. },
  39. template: '<p>{{a}}</p>'
  40. }
  41. }
  42. })
  43. expect(el.innerHTML).toBe('<p>123</p>')
  44. })
  45. it('allow v-component on table elements', function () {
  46. var vm = new Vue({
  47. el: el,
  48. template: '<table><tbody><tr v-component="test"></tr></tbody></table>',
  49. components: {
  50. test: {
  51. data: function () {
  52. return { a: 123 }
  53. },
  54. template: '<td>{{a}}</td>'
  55. }
  56. }
  57. })
  58. expect(el.innerHTML).toBe(vm.$options.template.replace(/<tr.*\/tr>/, '<tr><td>123</td></tr>'))
  59. expect(_.warn).not.toHaveBeenCalled()
  60. })
  61. it('inline-template', function () {
  62. var vm = new Vue({
  63. el: el,
  64. template: '<test inline-template>{{a}}</test>',
  65. data: {
  66. a: 'parent'
  67. },
  68. components: {
  69. test: {
  70. data: function () {
  71. return { a: 'child' }
  72. },
  73. template: 'child option template'
  74. }
  75. }
  76. })
  77. expect(el.innerHTML).toBe('<test>child</test>')
  78. })
  79. it('block replace', function () {
  80. var vm = new Vue({
  81. el: el,
  82. template: '<test></test>',
  83. components: {
  84. test: {
  85. replace: true,
  86. data: function () {
  87. return { a: 123, b: 234 }
  88. },
  89. template: '<p>{{a}}</p><p>{{b}}</p>'
  90. }
  91. }
  92. })
  93. expect(el.innerHTML).toBe('<p>123</p><p>234</p>')
  94. })
  95. it('dynamic', function (done) {
  96. var vm = new Vue({
  97. el: el,
  98. template: '<component is="{{view}}" v-attr="view:view"></component>',
  99. data: {
  100. view: 'view-a'
  101. },
  102. components: {
  103. 'view-a': {
  104. template: '<div>AAA</div>',
  105. replace: true,
  106. data: function () {
  107. return { view: 'a' }
  108. }
  109. },
  110. 'view-b': {
  111. template: '<div>BBB</div>',
  112. replace: true,
  113. data: function () {
  114. return { view: 'b' }
  115. }
  116. }
  117. }
  118. })
  119. expect(el.innerHTML).toBe('<div view="view-a">AAA</div>')
  120. vm.view = 'view-b'
  121. _.nextTick(function () {
  122. expect(el.innerHTML).toBe('<div view="view-b">BBB</div>')
  123. vm.view = ''
  124. _.nextTick(function () {
  125. expect(el.innerHTML).toBe('')
  126. done()
  127. })
  128. })
  129. })
  130. it('keep-alive', function (done) {
  131. var spyA = jasmine.createSpy()
  132. var spyB = jasmine.createSpy()
  133. var vm = new Vue({
  134. el: el,
  135. template: '<component is="{{view}}" keep-alive></component>',
  136. data: {
  137. view: 'view-a'
  138. },
  139. components: {
  140. 'view-a': {
  141. created: spyA,
  142. template: '<div>AAA</div>',
  143. replace: true
  144. },
  145. 'view-b': {
  146. created: spyB,
  147. template: '<div>BBB</div>',
  148. replace: true
  149. }
  150. }
  151. })
  152. expect(el.innerHTML).toBe('<div>AAA</div>')
  153. expect(spyA.calls.count()).toBe(1)
  154. expect(spyB.calls.count()).toBe(0)
  155. vm.view = 'view-b'
  156. _.nextTick(function () {
  157. expect(el.innerHTML).toBe('<div>BBB</div>')
  158. expect(spyA.calls.count()).toBe(1)
  159. expect(spyB.calls.count()).toBe(1)
  160. vm.view = 'view-a'
  161. _.nextTick(function () {
  162. expect(el.innerHTML).toBe('<div>AAA</div>')
  163. expect(spyA.calls.count()).toBe(1)
  164. expect(spyB.calls.count()).toBe(1)
  165. vm.view = 'view-b'
  166. _.nextTick(function () {
  167. expect(el.innerHTML).toBe('<div>BBB</div>')
  168. expect(spyA.calls.count()).toBe(1)
  169. expect(spyB.calls.count()).toBe(1)
  170. done()
  171. })
  172. })
  173. })
  174. })
  175. it('should compile parent template directives & content in parent scope', function (done) {
  176. var vm = new Vue({
  177. el: el,
  178. data: {
  179. ok: false,
  180. message: 'hello'
  181. },
  182. template: '<test v-show="ok">{{message}}</test>',
  183. components: {
  184. test: {
  185. template: '<div><content></content> {{message}}</div>',
  186. replace: true,
  187. data: function () {
  188. return {
  189. message: 'world'
  190. }
  191. }
  192. }
  193. }
  194. })
  195. expect(el.firstChild.style.display).toBe('none')
  196. expect(el.firstChild.textContent).toBe('hello world')
  197. vm.ok = true
  198. vm.message = 'bye'
  199. _.nextTick(function () {
  200. expect(el.firstChild.style.display).toBe('')
  201. expect(el.firstChild.textContent).toBe('bye world')
  202. done()
  203. })
  204. })
  205. it('parent content + v-if', function (done) {
  206. var vm = new Vue({
  207. el: el,
  208. data: {
  209. ok: false,
  210. message: 'hello'
  211. },
  212. template: '<test v-if="ok">{{message}}</test>',
  213. components: {
  214. test: {
  215. template: '<content></content> {{message}}',
  216. data: function () {
  217. return {
  218. message: 'world'
  219. }
  220. }
  221. }
  222. }
  223. })
  224. expect(el.textContent).toBe('')
  225. expect(vm.$children.length).toBe(0)
  226. expect(vm._directives.length).toBe(1) // v-if
  227. vm.ok = true
  228. _.nextTick(function () {
  229. expect(vm.$children.length).toBe(1)
  230. expect(vm._directives.length).toBe(3) // v-if, v-component, v-text
  231. expect(el.textContent).toBe('hello world')
  232. done()
  233. })
  234. })
  235. it('props', function () {
  236. var vm = new Vue({
  237. el: el,
  238. data: {
  239. list: [{a:1}, {a:2}]
  240. },
  241. template: '<test collection="{{list}}"></test>',
  242. components: {
  243. test: {
  244. template: '<ul><li v-repeat="collection">{{a}}</li></ul>',
  245. replace: true,
  246. props: ['collection']
  247. }
  248. }
  249. })
  250. expect(el.innerHTML).toBe('<ul><li>1</li><li>2</li></ul>')
  251. })
  252. it('wait-for for static component', function () {
  253. var vm = new Vue({
  254. el: el,
  255. template: '<view-a wait-for="ok"></view-a>',
  256. components: {
  257. 'view-a': {
  258. template: 'AAA'
  259. }
  260. }
  261. })
  262. expect(el.textContent).toBe('')
  263. vm.$children[0].$emit('ok')
  264. expect(el.textContent).toBe('AAA')
  265. })
  266. it('wait-for for dynamic components', function (done) {
  267. var vm = new Vue({
  268. el: el,
  269. data: {
  270. view: 'view-a'
  271. },
  272. template: '<component is="{{view}}" wait-for="ok"></component>',
  273. components: {
  274. 'view-a': {
  275. template: 'AAA'
  276. },
  277. 'view-b': {
  278. template: 'BBB'
  279. }
  280. }
  281. })
  282. vm.$children[0].$emit('ok')
  283. vm.view = 'view-b'
  284. _.nextTick(function () {
  285. expect(el.textContent).toBe('AAA')
  286. // old vm is already removed, this is the new vm
  287. vm.$children[0].$emit('ok')
  288. expect(el.textContent).toBe('BBB')
  289. done()
  290. })
  291. })
  292. it('transition-mode: in-out', function (done) {
  293. var spy1 = jasmine.createSpy('enter')
  294. var spy2 = jasmine.createSpy('leave')
  295. var next
  296. var vm = new Vue({
  297. el: el,
  298. data: {
  299. view: 'view-a'
  300. },
  301. template: '<component is="{{view}}" v-transition="test" transition-mode="in-out"></component>',
  302. components: {
  303. 'view-a': { template: 'AAA' },
  304. 'view-b': { template: 'BBB' }
  305. },
  306. transitions: {
  307. test: {
  308. enter: function (el, done) {
  309. spy1()
  310. next = done
  311. },
  312. leave: function (el, done) {
  313. spy2()
  314. _.nextTick(done)
  315. }
  316. }
  317. }
  318. })
  319. expect(el.textContent).toBe('AAA')
  320. vm.view = 'view-b'
  321. _.nextTick(function () {
  322. expect(spy1).toHaveBeenCalled()
  323. expect(spy2).not.toHaveBeenCalled()
  324. expect(el.textContent).toBe('AAABBB')
  325. next()
  326. _.nextTick(function () {
  327. expect(spy2).toHaveBeenCalled()
  328. _.nextTick(function () {
  329. expect(el.textContent).toBe('BBB')
  330. done()
  331. })
  332. })
  333. })
  334. })
  335. it('transition-mode: out-in', function (done) {
  336. var spy1 = jasmine.createSpy('enter')
  337. var spy2 = jasmine.createSpy('leave')
  338. var next
  339. var vm = new Vue({
  340. el: el,
  341. data: {
  342. view: 'view-a'
  343. },
  344. template: '<component is="{{view}}" v-transition="test" transition-mode="out-in"></component>',
  345. components: {
  346. 'view-a': { template: 'AAA' },
  347. 'view-b': { template: 'BBB' }
  348. },
  349. transitions: {
  350. test: {
  351. enter: function (el, done) {
  352. spy2()
  353. _.nextTick(done)
  354. },
  355. leave: function (el, done) {
  356. spy1()
  357. next = done
  358. }
  359. }
  360. }
  361. })
  362. expect(el.textContent).toBe('AAA')
  363. vm.view = 'view-b'
  364. _.nextTick(function () {
  365. expect(spy1).toHaveBeenCalled()
  366. expect(spy2).not.toHaveBeenCalled()
  367. expect(el.textContent).toBe('AAA')
  368. next()
  369. expect(spy2).toHaveBeenCalled()
  370. expect(el.textContent).toBe('BBB')
  371. done()
  372. })
  373. })
  374. it('teardown', function (done) {
  375. var vm = new Vue({
  376. el: el,
  377. template: '<component is="{{view}}" keep-alive></component>',
  378. data: {
  379. view: 'test'
  380. },
  381. components: {
  382. test: {},
  383. test2: {}
  384. }
  385. })
  386. vm.view = 'test2'
  387. _.nextTick(function () {
  388. expect(vm.$children.length).toBe(2)
  389. var child = vm.$children[0]
  390. var child2 = vm.$children[1]
  391. vm._directives[0].unbind()
  392. expect(vm._directives[0].cache).toBeNull()
  393. expect(vm.$children.length).toBe(0)
  394. expect(child._isDestroyed).toBe(true)
  395. expect(child2._isDestroyed).toBe(true)
  396. done()
  397. })
  398. })
  399. it('already mounted warn', function () {
  400. el.setAttribute('v-_component', 'test')
  401. var vm = new Vue({
  402. el: el
  403. })
  404. expect(hasWarned(_, 'Do not create a component that only contains a single other component')).toBe(true)
  405. })
  406. })
  407. }