mixin.spec.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import Vue from 'vue'
  2. describe('Global API: mixin', () => {
  3. let options
  4. beforeEach(() => {
  5. options = Vue.options
  6. })
  7. afterEach(() => {
  8. Vue.options = options
  9. })
  10. it('should work', () => {
  11. const spy = vi.fn()
  12. Vue.mixin({
  13. created() {
  14. spy(this.$options.myOption)
  15. }
  16. })
  17. new Vue({
  18. myOption: 'hello'
  19. })
  20. expect(spy).toHaveBeenCalledWith('hello')
  21. })
  22. it('should work for constructors created before mixin is applied', () => {
  23. const calls: any[] = []
  24. const Test: Vue = Vue.extend({
  25. name: 'test',
  26. beforeCreate() {
  27. calls.push(this.$options.myOption + ' local')
  28. }
  29. })
  30. Vue.mixin({
  31. beforeCreate() {
  32. calls.push(this.$options.myOption + ' global')
  33. }
  34. })
  35. expect(Test.options.name).toBe('test')
  36. new Test({
  37. myOption: 'hello'
  38. })
  39. expect(calls).toEqual(['hello global', 'hello local'])
  40. })
  41. // #3957
  42. it('should work for global props', () => {
  43. const Test = Vue.extend({
  44. template: `<div>{{ prop }}</div>`
  45. })
  46. Vue.mixin({
  47. props: ['prop']
  48. })
  49. // test child component
  50. const vm = new Vue({
  51. template: '<test prop="hi"></test>',
  52. components: { Test }
  53. }).$mount()
  54. expect(vm.$el.textContent).toBe('hi')
  55. })
  56. // vue-loader#433
  57. it('should not drop late-set render functions', () => {
  58. const Test = Vue.extend({})
  59. Test.options.render = h => h('div', 'hello')
  60. Vue.mixin({})
  61. const vm = new Vue({
  62. render: h => h(Test)
  63. }).$mount()
  64. expect(vm.$el.textContent).toBe('hello')
  65. })
  66. // #4266
  67. it('should not drop scopedId', () => {
  68. const Test = Vue.extend({})
  69. Test.options._scopeId = 'foo'
  70. Vue.mixin({})
  71. const vm = new Test({
  72. template: '<div><p>hi</p></div>'
  73. }).$mount()
  74. expect(vm.$el.children[0].hasAttribute('foo')).toBe(true)
  75. })
  76. // #4976
  77. it('should not drop late-attached custom options on existing constructors', () => {
  78. const baseSpy = vi.fn()
  79. const Base = Vue.extend({
  80. beforeCreate: baseSpy
  81. })
  82. const Test = Base.extend({})
  83. // Inject options later
  84. // vue-loader and vue-hot-reload-api are doing like this
  85. Test.options.computed = {
  86. $style: () => 123
  87. }
  88. const spy = vi.fn()
  89. Test.options.beforeCreate = Test.options.beforeCreate.concat(spy)
  90. // Update super constructor's options
  91. const mixinSpy = vi.fn()
  92. Vue.mixin({
  93. beforeCreate: mixinSpy
  94. })
  95. // mount the component
  96. const vm = new Test({
  97. template: '<div>{{ $style }}</div>'
  98. }).$mount()
  99. expect(spy.mock.calls.length).toBe(1)
  100. expect(baseSpy.mock.calls.length).toBe(1)
  101. expect(mixinSpy.mock.calls.length).toBe(1)
  102. expect(vm.$el.textContent).toBe('123')
  103. expect(vm.$style).toBe(123)
  104. // Should not be dropped
  105. expect(Test.options.computed.$style()).toBe(123)
  106. expect(Test.options.beforeCreate).toEqual([mixinSpy, baseSpy, spy])
  107. })
  108. // vue-class-component#83
  109. it('should work for a constructor mixin', () => {
  110. const spy = vi.fn()
  111. const Mixin = Vue.extend({
  112. created() {
  113. spy(this.$options.myOption)
  114. }
  115. })
  116. Vue.mixin(Mixin)
  117. new Vue({
  118. myOption: 'hello'
  119. })
  120. expect(spy).toHaveBeenCalledWith('hello')
  121. })
  122. // vue-class-component#87
  123. it('should not drop original lifecycle hooks', () => {
  124. const base = vi.fn()
  125. const Base = Vue.extend({
  126. beforeCreate: base
  127. })
  128. const injected = vi.fn()
  129. // inject a function
  130. Base.options.beforeCreate = Base.options.beforeCreate.concat(injected)
  131. Vue.mixin({})
  132. new Base({})
  133. expect(base).toHaveBeenCalled()
  134. expect(injected).toHaveBeenCalled()
  135. })
  136. // #8595
  137. it('chain call', () => {
  138. expect(Vue.mixin({})).toBe(Vue)
  139. })
  140. // #9198
  141. it('should not mix global mixin lifecycle hook twice', () => {
  142. const spy = vi.fn()
  143. Vue.mixin({
  144. created: spy
  145. })
  146. const mixin1 = Vue.extend({
  147. methods: {
  148. a() {}
  149. }
  150. })
  151. const mixin2 = Vue.extend({
  152. mixins: [mixin1]
  153. })
  154. const Child = Vue.extend({
  155. mixins: [mixin2]
  156. })
  157. const vm = new Child()
  158. expect(typeof vm.$options.methods.a).toBe('function')
  159. expect(spy.mock.calls.length).toBe(1)
  160. })
  161. })