properties.spec.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import Vue from 'vue'
  2. describe('Instance properties', () => {
  3. it('$data', () => {
  4. const data = { a: 1 }
  5. const vm = new Vue({
  6. data
  7. })
  8. expect(vm.a).toBe(1)
  9. expect(vm.$data).toBe(data)
  10. // vm -> data
  11. vm.a = 2
  12. expect(data.a).toBe(2)
  13. // data -> vm
  14. data.a = 3
  15. expect(vm.a).toBe(3)
  16. })
  17. it('$options', () => {
  18. const A = Vue.extend({
  19. methods: {
  20. a() {}
  21. }
  22. })
  23. const vm = new A({
  24. methods: {
  25. b() {}
  26. }
  27. })
  28. expect(typeof vm.$options.methods?.a).toBe('function')
  29. expect(typeof vm.$options.methods?.b).toBe('function')
  30. })
  31. it('$root/$children', done => {
  32. const vm = new Vue({
  33. template: '<div><test v-if="ok"></test></div>',
  34. data: { ok: true },
  35. components: {
  36. test: {
  37. template: '<div></div>'
  38. }
  39. }
  40. }).$mount()
  41. expect(vm.$root).toBe(vm)
  42. expect(vm.$children.length).toBe(1)
  43. expect(vm.$children[0].$root).toBe(vm)
  44. vm.ok = false
  45. waitForUpdate(() => {
  46. expect(vm.$children.length).toBe(0)
  47. vm.ok = true
  48. })
  49. .then(() => {
  50. expect(vm.$children.length).toBe(1)
  51. expect(vm.$children[0].$root).toBe(vm)
  52. })
  53. .then(done)
  54. })
  55. it('$parent', () => {
  56. const calls: any[] = []
  57. const makeOption = name => ({
  58. name,
  59. template: `<div><slot></slot></div>`,
  60. created() {
  61. calls.push(`${name}:${this.$parent.$options.name}`)
  62. }
  63. })
  64. new Vue({
  65. template: `
  66. <div>
  67. <outer><middle><inner></inner></middle></outer>
  68. <next></next>
  69. </div>
  70. `,
  71. components: {
  72. outer: makeOption('outer'),
  73. middle: makeOption('middle'),
  74. inner: makeOption('inner'),
  75. next: makeOption('next')
  76. }
  77. }).$mount()
  78. expect(calls).toEqual([
  79. 'outer:undefined',
  80. 'middle:outer',
  81. 'inner:middle',
  82. 'next:undefined'
  83. ])
  84. })
  85. it('$props', done => {
  86. const Comp = Vue.extend({
  87. props: ['msg'],
  88. template: '<div>{{ msg }} {{ $props.msg }}</div>'
  89. })
  90. const vm = new Comp({
  91. propsData: {
  92. msg: 'foo'
  93. }
  94. }).$mount()
  95. // check render
  96. expect(vm.$el.textContent).toContain('foo foo')
  97. // warn set
  98. vm.$props = {}
  99. expect('$props is readonly').toHaveBeenWarned()
  100. // check existence
  101. expect(vm.$props.msg).toBe('foo')
  102. // check change
  103. vm.msg = 'bar'
  104. expect(vm.$props.msg).toBe('bar')
  105. waitForUpdate(() => {
  106. expect(vm.$el.textContent).toContain('bar bar')
  107. })
  108. .then(() => {
  109. vm.$props.msg = 'baz'
  110. expect(vm.msg).toBe('baz')
  111. })
  112. .then(() => {
  113. expect(vm.$el.textContent).toContain('baz baz')
  114. })
  115. .then(done)
  116. })
  117. it('warn mutating $props', () => {
  118. const Comp = {
  119. props: ['msg'],
  120. render() {},
  121. mounted() {
  122. expect(this.$props.msg).toBe('foo')
  123. this.$props.msg = 'bar'
  124. }
  125. }
  126. new Vue({
  127. template: `<comp ref="comp" msg="foo" />`,
  128. components: { Comp }
  129. }).$mount()
  130. expect(`Avoid mutating a prop`).toHaveBeenWarned()
  131. })
  132. it('$attrs', done => {
  133. const vm = new Vue({
  134. template: `<foo :id="foo" bar="1"/>`,
  135. data: { foo: 'foo' },
  136. components: {
  137. foo: {
  138. props: ['bar'],
  139. template: `<div><div v-bind="$attrs"></div></div>`
  140. }
  141. }
  142. }).$mount()
  143. expect(vm.$el.children[0].id).toBe('foo')
  144. expect(vm.$el.children[0].hasAttribute('bar')).toBe(false)
  145. vm.foo = 'bar'
  146. waitForUpdate(() => {
  147. expect(vm.$el.children[0].id).toBe('bar')
  148. expect(vm.$el.children[0].hasAttribute('bar')).toBe(false)
  149. }).then(done)
  150. })
  151. // #6263
  152. it('$attrs should not be undefined when no props passed in', () => {
  153. const vm = new Vue({
  154. template: `<foo ref="foo" />`,
  155. data: { foo: 'foo' },
  156. components: {
  157. foo: {
  158. template: `<div>foo</div>`
  159. }
  160. }
  161. }).$mount()
  162. expect(vm.$refs.foo.$attrs).toBeDefined()
  163. })
  164. it('warn mutating $attrs', () => {
  165. const vm = new Vue()
  166. vm.$attrs = {}
  167. expect(`$attrs is readonly`).toHaveBeenWarned()
  168. })
  169. it('$listeners', done => {
  170. const spyA = vi.fn()
  171. const spyB = vi.fn()
  172. const vm = new Vue({
  173. template: `<foo @click="foo"/>`,
  174. data: { foo: spyA },
  175. components: {
  176. foo: {
  177. template: `<div v-on="$listeners"></div>`
  178. }
  179. }
  180. }).$mount()
  181. // has to be in dom for test to pass in IE
  182. document.body.appendChild(vm.$el)
  183. triggerEvent(vm.$el, 'click')
  184. expect(spyA.mock.calls.length).toBe(1)
  185. expect(spyB.mock.calls.length).toBe(0)
  186. vm.foo = spyB
  187. waitForUpdate(() => {
  188. triggerEvent(vm.$el, 'click')
  189. expect(spyA.mock.calls.length).toBe(1)
  190. expect(spyB.mock.calls.length).toBe(1)
  191. document.body.removeChild(vm.$el)
  192. }).then(done)
  193. })
  194. it('warn mutating $listeners', () => {
  195. const vm = new Vue()
  196. vm.$listeners = {}
  197. expect(`$listeners is readonly`).toHaveBeenWarned()
  198. })
  199. })