misc_spec.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. // test cases for edge cases & bug fixes
  2. var Vue = require('../../../src/index')
  3. var _ = Vue.util
  4. describe('Misc', function () {
  5. beforeEach(function () {
  6. spyWarns()
  7. })
  8. it('should handle directive.bind() altering its childNode structure', function () {
  9. var vm = new Vue({
  10. el: document.createElement('div'),
  11. template: '<div v-test>{{test}}</div>',
  12. data: {
  13. test: 'hi'
  14. },
  15. directives: {
  16. test: {
  17. bind: function () {
  18. this.el.insertBefore(document.createTextNode('yo '),
  19. this.el.firstChild)
  20. }
  21. }
  22. }
  23. })
  24. expect(vm.$el.textContent).toBe('yo hi')
  25. })
  26. it('attached/detached hooks for transcluded components', function () {
  27. var spy1 = jasmine.createSpy('attached')
  28. var spy2 = jasmine.createSpy('detached')
  29. var el = document.createElement('div')
  30. el.innerHTML = '<outer v-ref:outter><inner></inner></outer>'
  31. document.body.appendChild(el)
  32. var vm = new Vue({
  33. el: el,
  34. components: {
  35. outer: {
  36. template: '<slot></slot>'
  37. },
  38. inner: {
  39. template: 'hi',
  40. attached: spy1,
  41. detached: spy2
  42. }
  43. }
  44. })
  45. expect(spy1).toHaveBeenCalled()
  46. vm.$refs.outter.$remove()
  47. expect(spy2).toHaveBeenCalled()
  48. })
  49. it('v-for on component root node with replace:true', function () {
  50. var el = document.createElement('div')
  51. var vm = new Vue({
  52. el: el,
  53. template: '<test></test>',
  54. components: {
  55. test: {
  56. data: function () {
  57. return { list: [1, 2, 3] }
  58. },
  59. template: '<div v-for="n in list">{{n}}</div>',
  60. replace: true
  61. }
  62. }
  63. })
  64. expect(vm.$el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
  65. })
  66. // #922
  67. it('template v-for inside svg', function () {
  68. var el = document.createElement('div')
  69. new Vue({
  70. el: el,
  71. template: '<svg><template v-for="n in list"><text>{{n}}</text></template></svg>',
  72. data: {
  73. list: [1, 2, 3]
  74. }
  75. })
  76. // IE inlines svg namespace
  77. var xmlns = /\s?xmlns=".*svg"/
  78. expect(el.innerHTML.replace(xmlns, '')).toBe('<svg><text>1</text><text>2</text><text>3</text></svg>')
  79. })
  80. // #1005
  81. it('call lifecycle hooks for child components', function () {
  82. Vue.options.replace = true
  83. var el = document.createElement('div')
  84. var logs = []
  85. function log (n) {
  86. return function () {
  87. logs.push(n)
  88. }
  89. }
  90. document.body.appendChild(el)
  91. var vm = new Vue({
  92. el: el,
  93. attached: log(0),
  94. ready: log(1),
  95. detached: log(2),
  96. beforeDestroy: log(3),
  97. destroyed: log(4),
  98. template: '<div><test></test><test></test></div>',
  99. components: {
  100. test: {
  101. template: '<span>hi</span>',
  102. attached: log(5),
  103. ready: log(6),
  104. detached: log(7),
  105. beforeDestroy: log(8),
  106. destroyed: log(9)
  107. }
  108. }
  109. })
  110. expect(vm.$el.innerHTML).toBe('<span>hi</span><span>hi</span>')
  111. expect(logs.join()).toBe('0,5,6,5,6,1')
  112. logs = []
  113. vm.$destroy(true)
  114. expect(logs.join()).toBe('3,8,9,8,9,2,7,7,4')
  115. Vue.options.replace = false
  116. })
  117. // #1006
  118. it('destroyed hook for components inside v-if', function (done) {
  119. var spy = jasmine.createSpy('v-if destroyed hook')
  120. var vm = new Vue({
  121. el: document.createElement('div'),
  122. template: '<template v-if="ok"><test></test></template>',
  123. data: {
  124. ok: true
  125. },
  126. components: {
  127. test: {
  128. destroyed: spy
  129. }
  130. }
  131. })
  132. vm.ok = false
  133. Vue.nextTick(function () {
  134. expect(spy).toHaveBeenCalled()
  135. done()
  136. })
  137. })
  138. it('frozen model, root', function (done) {
  139. var vm = new Vue({
  140. el: document.createElement('div'),
  141. template: '{{msg}}',
  142. data: Object.freeze({
  143. msg: 'hi!'
  144. })
  145. })
  146. expect(vm.$el.textContent).toBe('hi!')
  147. try { vm.msg = 'ho!' } catch (e) {}
  148. Vue.nextTick(function () {
  149. expect(vm.$el.textContent).toBe('hi!')
  150. done()
  151. })
  152. })
  153. it('frozen model, non-root', function (done) {
  154. var vm = new Vue({
  155. el: document.createElement('div'),
  156. template: '{{msg}} {{frozen.msg}}',
  157. data: {
  158. msg: 'hi',
  159. frozen: Object.freeze({
  160. msg: 'frozen'
  161. })
  162. }
  163. })
  164. expect(vm.$el.textContent).toBe('hi frozen')
  165. vm.msg = 'ho'
  166. vm.frozen.msg = 'changed'
  167. Vue.nextTick(function () {
  168. expect(vm.$el.textContent).toBe('ho frozen')
  169. done()
  170. })
  171. })
  172. it('should not trigger deep/Array watchers when digesting', function (done) {
  173. var spy1 = jasmine.createSpy('deep')
  174. var spy2 = jasmine.createSpy('Array')
  175. var spy3 = jasmine.createSpy('test')
  176. var spy4 = jasmine.createSpy('deep-mutated')
  177. var vm = new Vue({
  178. el: document.createElement('div'),
  179. data: {
  180. obj: {},
  181. arr: [],
  182. obj2: {}
  183. },
  184. watch: {
  185. obj: {
  186. handler: spy1,
  187. deep: true
  188. },
  189. arr: spy2,
  190. // if the watcher is watching the added value,
  191. // it should still trigger properly
  192. test: {
  193. handler: spy3,
  194. deep: true
  195. },
  196. // if the object is in fact mutated, it should
  197. // still trigger.
  198. obj2: {
  199. handler: spy4,
  200. deep: true
  201. }
  202. }
  203. })
  204. var test = []
  205. var obj2 = vm.obj2
  206. vm.$set('test', test)
  207. _.set(obj2, 'test', 123)
  208. Vue.nextTick(function () {
  209. expect(spy1).not.toHaveBeenCalled()
  210. expect(spy2).not.toHaveBeenCalled()
  211. expect(spy3).toHaveBeenCalledWith(test, undefined)
  212. expect(spy4).toHaveBeenCalledWith(obj2, obj2)
  213. done()
  214. })
  215. })
  216. it('handle interpolated textarea', function (done) {
  217. var el = document.createElement('div')
  218. el.innerHTML = '<textarea>hello {{msg}}</textarea>'
  219. var vm = new Vue({
  220. el: el,
  221. data: {
  222. msg: 'test'
  223. }
  224. })
  225. expect(el.innerHTML).toBe('<textarea>hello test</textarea>')
  226. vm.msg = 'world'
  227. Vue.nextTick(function () {
  228. expect(el.innerHTML).toBe('<textarea>hello world</textarea>')
  229. done()
  230. })
  231. })
  232. it('nested object $set should trigger parent array notify', function (done) {
  233. var vm = new Vue({
  234. el: document.createElement('div'),
  235. template: '{{items | json}}{{items[0].a}}',
  236. data: {
  237. items: [{}]
  238. }
  239. })
  240. expect(vm.$el.textContent).toBe(JSON.stringify(vm.items, null, 2))
  241. _.set(vm.items[0], 'a', 123)
  242. Vue.nextTick(function () {
  243. expect(vm.$el.textContent).toBe(JSON.stringify(vm.items, null, 2) + '123')
  244. done()
  245. })
  246. })
  247. it('warn unkown custom element', function () {
  248. new Vue({
  249. el: document.createElement('div'),
  250. template: '<custom-stuff></custom-stuff>'
  251. })
  252. expect(hasWarned('Unknown custom element')).toBe(true)
  253. })
  254. it('prefer bound attributes over static attributes', function (done) {
  255. var el = document.createElement('div')
  256. var count = 0
  257. var expected = [
  258. 'bound',
  259. 'bound',
  260. 'static',
  261. 'bound',
  262. 'bound'
  263. ]
  264. function check (title) {
  265. expect(title).toBe(expected[count])
  266. count++
  267. if (count === 4) {
  268. done()
  269. }
  270. }
  271. new Vue({
  272. el: el,
  273. template:
  274. '<div>\
  275. <comp v-bind:title="title"></comp>\
  276. <comp title="static" v-bind:title="title"></comp>\
  277. <comp title="static"></comp>\
  278. <comp :title="title"></comp>\
  279. <comp title="static" :title="title"></comp>\
  280. </div>',
  281. data: {
  282. title: 'bound'
  283. },
  284. components: {
  285. comp: {
  286. props: ['title'],
  287. created: function () {
  288. check(this.title)
  289. }
  290. }
  291. }
  292. })
  293. })
  294. it('deep watch for class, style and bind', function (done) {
  295. var el = document.createElement('div')
  296. var vm = new Vue({
  297. el: el,
  298. template: '<div :class="classes" :style="styles" v-bind="attrs"></div>',
  299. data: {
  300. classes: { a: true, b: false },
  301. styles: { color: 'red', fontSize: '14px' },
  302. attrs: { a: 1, b: 2 }
  303. }
  304. })
  305. var div = el.firstChild
  306. expect(div.className).toBe('a')
  307. expect(div.style.color).toBe('red')
  308. expect(div.style.fontSize).toBe('14px')
  309. expect(div.getAttribute('a')).toBe('1')
  310. expect(div.getAttribute('b')).toBe('2')
  311. vm.classes.b = true
  312. vm.styles.color = 'green'
  313. vm.attrs.a = 3
  314. Vue.nextTick(function () {
  315. expect(div.className).toBe('a b')
  316. expect(div.style.color).toBe('green')
  317. expect(div.style.fontSize).toBe('14px')
  318. expect(div.getAttribute('a')).toBe('3')
  319. expect(div.getAttribute('b')).toBe('2')
  320. done()
  321. })
  322. })
  323. })