if_spec.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. var Vue = require('src')
  2. var nextTick = Vue.nextTick
  3. describe('v-if', function () {
  4. var el
  5. beforeEach(function () {
  6. el = document.createElement('div')
  7. })
  8. it('normal', function (done) {
  9. var vm = new Vue({
  10. el: el,
  11. data: { test: false, a: 'A' },
  12. template: '<div v-if="test"><test :a="a"></test></div>',
  13. components: {
  14. test: {
  15. props: ['a'],
  16. template: '{{a}}'
  17. }
  18. }
  19. })
  20. // lazy instantitation
  21. expect(el.innerHTML).toBe('')
  22. expect(vm.$children.length).toBe(0)
  23. vm.test = true
  24. nextTick(function () {
  25. expect(el.innerHTML).toBe('<div><test>A</test></div>')
  26. expect(vm.$children.length).toBe(1)
  27. vm.test = false
  28. nextTick(function () {
  29. expect(el.innerHTML).toBe('')
  30. expect(vm.$children.length).toBe(0)
  31. vm.test = true
  32. nextTick(function () {
  33. expect(el.innerHTML).toBe('<div><test>A</test></div>')
  34. expect(vm.$children.length).toBe(1)
  35. var child = vm.$children[0]
  36. vm.$destroy()
  37. expect(child._isDestroyed).toBe(true)
  38. done()
  39. })
  40. })
  41. })
  42. })
  43. it('template block', function (done) {
  44. var vm = new Vue({
  45. el: el,
  46. data: { test: false, a: 'A', b: 'B' },
  47. template: '<template v-if="test"><p>{{a}}</p><p>{{b}}</p></template>'
  48. })
  49. // lazy instantitation
  50. expect(el.innerHTML).toBe('')
  51. vm.test = true
  52. nextTick(function () {
  53. expect(el.innerHTML).toBe('<p>A</p><p>B</p>')
  54. vm.test = false
  55. nextTick(function () {
  56. expect(el.innerHTML).toBe('')
  57. done()
  58. })
  59. })
  60. })
  61. it('v-if + component', function (done) {
  62. var attachSpy = jasmine.createSpy()
  63. var detachSpy = jasmine.createSpy()
  64. var readySpy = jasmine.createSpy()
  65. var vm = new Vue({
  66. el: el,
  67. data: { ok: false },
  68. template: '<test v-if="ok"></test>',
  69. components: {
  70. test: {
  71. data: function () {
  72. return { a: 123 }
  73. },
  74. template: '{{a}}',
  75. ready: readySpy,
  76. attached: attachSpy,
  77. detached: detachSpy
  78. }
  79. }
  80. })
  81. vm.$appendTo(document.body)
  82. expect(el.innerHTML).toBe('')
  83. expect(vm.$children.length).toBe(0)
  84. vm.ok = true
  85. nextTick(function () {
  86. expect(el.innerHTML).toBe('<test>123</test>')
  87. expect(vm.$children.length).toBe(1)
  88. expect(attachSpy).toHaveBeenCalled()
  89. expect(readySpy).toHaveBeenCalled()
  90. vm.ok = false
  91. nextTick(function () {
  92. expect(detachSpy).toHaveBeenCalled()
  93. expect(el.innerHTML).toBe('')
  94. expect(vm.$children.length).toBe(0)
  95. vm.$remove()
  96. done()
  97. })
  98. })
  99. })
  100. it('v-if + dynamic component', function (done) {
  101. var vm = new Vue({
  102. el: el,
  103. data: {
  104. ok: false,
  105. view: 'view-a'
  106. },
  107. template: '<component :is="view" v-if="ok"></component>',
  108. components: {
  109. 'view-a': {
  110. template: 'foo'
  111. },
  112. 'view-b': {
  113. template: 'bar'
  114. }
  115. }
  116. })
  117. expect(el.innerHTML).toBe('')
  118. expect(vm.$children.length).toBe(0)
  119. // toggle if with lazy instantiation
  120. vm.ok = true
  121. nextTick(function () {
  122. expect(el.innerHTML).toBe('<component>foo</component>')
  123. expect(vm.$children.length).toBe(1)
  124. // switch view when if=true
  125. vm.view = 'view-b'
  126. nextTick(function () {
  127. expect(el.innerHTML).toBe('<component>bar</component>')
  128. expect(vm.$children.length).toBe(1)
  129. // toggle if when already instantiated
  130. vm.ok = false
  131. nextTick(function () {
  132. expect(el.innerHTML).toBe('')
  133. expect(vm.$children.length).toBe(0)
  134. // toggle if and switch view at the same time
  135. vm.view = 'view-a'
  136. vm.ok = true
  137. nextTick(function () {
  138. expect(el.innerHTML).toBe('<component>foo</component>')
  139. expect(vm.$children.length).toBe(1)
  140. done()
  141. })
  142. })
  143. })
  144. })
  145. })
  146. it('v-if with different truthy values', function (done) {
  147. var vm = new Vue({
  148. el: el,
  149. data: {
  150. a: 1
  151. },
  152. template: '<div v-if="a">{{a}}</div>'
  153. })
  154. expect(el.innerHTML).toBe('<div>1</div>')
  155. vm.a = 2
  156. nextTick(function () {
  157. expect(el.innerHTML).toBe('<div>2</div>')
  158. done()
  159. })
  160. })
  161. it('invalid warn', function () {
  162. el.setAttribute('v-if', 'test')
  163. new Vue({
  164. el: el
  165. })
  166. expect('cannot be used on an instance root element').toHaveBeenWarned()
  167. })
  168. it('call attach/detach for transcluded components', function (done) {
  169. document.body.appendChild(el)
  170. var attachSpy = jasmine.createSpy('attached')
  171. var detachSpy = jasmine.createSpy('detached')
  172. var vm = new Vue({
  173. el: el,
  174. data: { show: true },
  175. template: '<outer><transcluded></transcluded></outer>',
  176. components: {
  177. outer: {
  178. template: '<div v-if="$parent.show"><slot></slot></div>'
  179. },
  180. transcluded: {
  181. template: 'transcluded',
  182. attached: attachSpy,
  183. detached: detachSpy
  184. }
  185. }
  186. })
  187. expect(attachSpy).toHaveBeenCalled()
  188. vm.show = false
  189. nextTick(function () {
  190. expect(detachSpy).toHaveBeenCalled()
  191. document.body.removeChild(el)
  192. done()
  193. })
  194. })
  195. it('call attach/detach for dynamicly created components inside if block', function (done) {
  196. document.body.appendChild(el)
  197. var attachSpy = jasmine.createSpy('attached')
  198. var detachSpy = jasmine.createSpy('detached')
  199. var transcluded = {
  200. props: ['a'],
  201. template: '{{a}}',
  202. attached: attachSpy,
  203. detached: detachSpy
  204. }
  205. var vm = new Vue({
  206. el: el,
  207. data: {
  208. show: true,
  209. list: [{a: 0}]
  210. },
  211. template:
  212. '<outer>' +
  213. '<div>' + // an extra layer to test components deep inside the tree
  214. '<transcluded v-for="item in list" :a="item.a"></transcluded>' +
  215. '</div>' +
  216. '</outer>',
  217. components: {
  218. outer: {
  219. template:
  220. '<div v-if="$parent.show">' +
  221. '<slot></slot>' +
  222. '</div>' +
  223. // this is to test that compnents that are not in the if block
  224. // should not fire attach/detach when v-if toggles
  225. '<transcluded></transcluded>',
  226. components: {
  227. transcluded: transcluded
  228. }
  229. },
  230. transcluded: transcluded
  231. }
  232. })
  233. assertMarkup()
  234. expect(attachSpy.calls.count()).toBe(2)
  235. vm.show = false
  236. nextTick(function () {
  237. assertMarkup()
  238. expect(detachSpy.calls.count()).toBe(1)
  239. vm.list.push({a: 1})
  240. vm.show = true
  241. nextTick(function () {
  242. assertMarkup()
  243. expect(attachSpy.calls.count()).toBe(2 + 2)
  244. vm.list.push({a: 2})
  245. vm.show = false
  246. nextTick(function () {
  247. assertMarkup()
  248. expect(attachSpy.calls.count()).toBe(2 + 2 + 1)
  249. expect(detachSpy.calls.count()).toBe(1 + 3)
  250. document.body.removeChild(el)
  251. done()
  252. })
  253. })
  254. })
  255. function assertMarkup () {
  256. var showBlock = vm.show
  257. ? '<div><div>' +
  258. vm.list.map(function (o) {
  259. return '<transcluded>' + o.a + '</transcluded>'
  260. }).join('') +
  261. '</div></div>'
  262. : ''
  263. var markup =
  264. '<outer>' +
  265. showBlock +
  266. '<transcluded></transcluded>' +
  267. '</outer>'
  268. expect(el.innerHTML).toBe(markup)
  269. }
  270. })
  271. it('call attach/detach for nested ifs', function (done) {
  272. var attachSpy = jasmine.createSpy('attached')
  273. var detachSpy = jasmine.createSpy('detached')
  274. document.body.appendChild(el)
  275. var vm = new Vue({
  276. el: el,
  277. data: {
  278. showOuter: true,
  279. showInner: false
  280. },
  281. template:
  282. '<div v-if="showOuter">' +
  283. '<div v-if="showInner">' +
  284. '<test></test>' +
  285. '</div>' +
  286. '</div>',
  287. components: {
  288. test: {
  289. attached: attachSpy,
  290. detached: detachSpy
  291. }
  292. }
  293. })
  294. expect(attachSpy).not.toHaveBeenCalled()
  295. vm.showInner = true
  296. nextTick(function () {
  297. expect(attachSpy.calls.count()).toBe(1)
  298. vm.showOuter = false
  299. nextTick(function () {
  300. expect(detachSpy.calls.count()).toBe(1)
  301. document.body.removeChild(el)
  302. done()
  303. })
  304. })
  305. })
  306. // #893 in IE textNodes do not have `contains` method
  307. it('call attach/detach: comparing textNodes in IE', function (done) {
  308. document.body.appendChild(el)
  309. var attachSpy = jasmine.createSpy('attached')
  310. var detachSpy = jasmine.createSpy('detached')
  311. var vm = new Vue({
  312. el: el,
  313. data: {
  314. show: true
  315. },
  316. template: '<template v-if="show"><test></test></template>',
  317. components: {
  318. test: {
  319. template: 'foo',
  320. replace: true,
  321. attached: attachSpy,
  322. detached: detachSpy
  323. }
  324. }
  325. })
  326. assertMarkup()
  327. assertCalls(1, 0)
  328. vm.show = false
  329. nextTick(function () {
  330. assertMarkup()
  331. assertCalls(1, 1)
  332. vm.show = true
  333. nextTick(function () {
  334. assertMarkup()
  335. assertCalls(2, 1)
  336. vm.show = false
  337. nextTick(function () {
  338. assertMarkup()
  339. assertCalls(2, 2)
  340. document.body.removeChild(el)
  341. done()
  342. })
  343. })
  344. })
  345. function assertMarkup () {
  346. expect(el.innerHTML).toBe(vm.show ? 'foo' : '')
  347. }
  348. function assertCalls (attach, detach) {
  349. expect(attachSpy.calls.count()).toBe(attach)
  350. expect(detachSpy.calls.count()).toBe(detach)
  351. }
  352. })
  353. // #1097 v-if components not having correct parent
  354. it('compile with correct transclusion host', function () {
  355. var parentA
  356. var parentB
  357. new Vue({
  358. el: el,
  359. data: {
  360. show: true
  361. },
  362. template: '<parent><child v-if="show"></child></parent>',
  363. components: {
  364. parent: {
  365. template: '<slot></slot>',
  366. created: function () {
  367. parentA = this
  368. }
  369. },
  370. child: {
  371. created: function () {
  372. parentB = this.$parent
  373. }
  374. }
  375. }
  376. })
  377. expect(parentA).toBeTruthy()
  378. expect(parentA).toBe(parentB)
  379. })
  380. it('if + else', function (done) {
  381. var vm = new Vue({
  382. el: el,
  383. data: { test: false, a: 'A', b: 'B' },
  384. template: '<div v-if="test">{{a}}</div><div v-else>{{b}}</div>'
  385. })
  386. expect(el.textContent).toBe('B')
  387. vm.test = true
  388. nextTick(function () {
  389. expect(el.textContent).toBe('A')
  390. vm.test = false
  391. nextTick(function () {
  392. expect(el.textContent).toBe('B')
  393. done()
  394. })
  395. })
  396. })
  397. it('else block teardown', function (done) {
  398. var created = jasmine.createSpy()
  399. var destroyed = jasmine.createSpy()
  400. var vm = new Vue({
  401. el: el,
  402. data: { ok: false },
  403. template: '<div v-if="ok"></div><div v-else><test></test></div>',
  404. components: {
  405. test: {
  406. created: created,
  407. destroyed: destroyed
  408. }
  409. }
  410. })
  411. expect(created.calls.count()).toBe(1)
  412. vm.$destroy()
  413. nextTick(function () {
  414. expect(destroyed.calls.count()).toBe(1)
  415. done()
  416. })
  417. })
  418. })