| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- import Vue from '@vue/compat'
- import { effect, isReactive } from '@vue/reactivity'
- import { h, nextTick } from '@vue/runtime-core'
- import {
- DeprecationTypes,
- deprecationData,
- toggleDeprecationWarning,
- } from '../../runtime-core/src/compat/compatConfig'
- import { singletonApp } from '../../runtime-core/src/compat/global'
- import { createApp } from '../src/esm-index'
- beforeEach(() => {
- toggleDeprecationWarning(false)
- Vue.configureCompat({ MODE: 2 })
- })
- afterEach(() => {
- Vue.configureCompat({ MODE: 3 })
- toggleDeprecationWarning(false)
- })
- describe('GLOBAL_MOUNT', () => {
- test('new Vue() with el', () => {
- toggleDeprecationWarning(true)
- const el = document.createElement('div')
- el.innerHTML = `{{ msg }}`
- new Vue({
- el,
- compatConfig: { GLOBAL_MOUNT: true },
- data() {
- return {
- msg: 'hello',
- }
- },
- })
- expect(
- deprecationData[DeprecationTypes.GLOBAL_MOUNT].message,
- ).toHaveBeenWarned()
- expect(el.innerHTML).toBe('hello')
- })
- test('new Vue() + $mount', () => {
- const el = document.createElement('div')
- el.innerHTML = `{{ msg }}`
- new Vue({
- data() {
- return {
- msg: 'hello',
- }
- },
- }).$mount(el)
- expect(el.innerHTML).toBe('hello')
- })
- })
- describe('GLOBAL_MOUNT_CONTAINER', () => {
- test('should warn', () => {
- toggleDeprecationWarning(true)
- const el = document.createElement('div')
- el.innerHTML = `test`
- el.setAttribute('v-bind:id', 'foo')
- new Vue().$mount(el)
- // warning only
- expect(
- deprecationData[DeprecationTypes.GLOBAL_MOUNT].message,
- ).toHaveBeenWarned()
- expect(
- deprecationData[DeprecationTypes.GLOBAL_MOUNT_CONTAINER].message,
- ).toHaveBeenWarned()
- })
- })
- describe('GLOBAL_EXTEND', () => {
- // https://github.com/vuejs/vue/blob/dev/test/unit/features/global-api/extend.spec.js
- it('should correctly merge options', () => {
- toggleDeprecationWarning(true)
- const Test = Vue.extend({
- name: 'test',
- a: 1,
- b: 2,
- })
- expect(Test.options.a).toBe(1)
- expect(Test.options.b).toBe(2)
- expect(Test.super).toBe(Vue)
- const t = new Test({
- a: 2,
- })
- expect(t.$options.a).toBe(2)
- expect(t.$options.b).toBe(2)
- // inheritance
- const Test2 = Test.extend({
- a: 2,
- })
- expect(Test2.options.a).toBe(2)
- expect(Test2.options.b).toBe(2)
- const t2 = new Test2({
- a: 3,
- })
- expect(t2.$options.a).toBe(3)
- expect(t2.$options.b).toBe(2)
- expect(
- deprecationData[DeprecationTypes.GLOBAL_MOUNT].message,
- ).toHaveBeenWarned()
- expect(
- deprecationData[DeprecationTypes.GLOBAL_EXTEND].message,
- ).toHaveBeenWarned()
- })
- it('should work when used as components', () => {
- const foo = Vue.extend({
- template: '<span>foo</span>',
- })
- const bar = Vue.extend({
- template: '<span>bar</span>',
- })
- const vm = new Vue({
- template: '<div><foo></foo><bar></bar></div>',
- components: { foo, bar },
- }).$mount()
- expect(vm.$el).toBeInstanceOf(HTMLDivElement)
- expect(vm.$el.innerHTML).toBe('<span>foo</span><span>bar</span>')
- })
- it('should merge lifecycle hooks', () => {
- const calls: number[] = []
- const A = Vue.extend({
- created() {
- calls.push(1)
- },
- })
- const B = A.extend({
- created() {
- calls.push(2)
- },
- })
- new B({
- created() {
- calls.push(3)
- },
- })
- expect(calls).toEqual([1, 2, 3])
- })
- it('should not merge nested mixins created with Vue.extend', () => {
- const a = vi.fn()
- const b = vi.fn()
- const c = vi.fn()
- const d = vi.fn()
- const A = Vue.extend({
- created: a,
- })
- const B = Vue.extend({
- mixins: [A],
- created: b,
- })
- const C = Vue.extend({
- extends: B,
- created: c,
- })
- const D = Vue.extend({
- mixins: [C],
- created: d,
- render() {
- return null
- },
- })
- new D().$mount()
- expect(a.mock.calls.length).toStrictEqual(1)
- expect(b.mock.calls.length).toStrictEqual(1)
- expect(c.mock.calls.length).toStrictEqual(1)
- expect(d.mock.calls.length).toStrictEqual(1)
- })
- it('should merge methods', () => {
- const A = Vue.extend({
- methods: {
- a() {
- return this.n
- },
- },
- })
- const B = A.extend({
- methods: {
- b() {
- return this.n + 1
- },
- },
- })
- const b = new B({
- data: () => ({ n: 0 }),
- methods: {
- c() {
- return this.n + 2
- },
- },
- }) as any
- expect(b.a()).toBe(0)
- expect(b.b()).toBe(1)
- expect(b.c()).toBe(2)
- })
- it('should merge assets', () => {
- const A = Vue.extend({
- components: {
- aa: {
- template: '<div>A</div>',
- },
- },
- })
- const B = A.extend({
- components: {
- bb: {
- template: '<div>B</div>',
- },
- },
- })
- const b = new B({
- template: '<div><aa></aa><bb></bb></div>',
- }).$mount()
- expect(b.$el).toBeInstanceOf(HTMLDivElement)
- expect(b.$el.innerHTML).toBe('<div>A</div><div>B</div>')
- })
- it('caching', () => {
- const options = {
- template: '<div></div>',
- }
- const A = Vue.extend(options)
- const B = Vue.extend(options)
- expect(A).toBe(B)
- })
- it('extended options should use different identify from parent', () => {
- const A = Vue.extend({ computed: {} })
- const B = A.extend()
- B.options.computed.b = () => 'foo'
- expect(B.options.computed).not.toBe(A.options.computed)
- expect(A.options.computed.b).toBeUndefined()
- })
- })
- describe('GLOBAL_PROTOTYPE', () => {
- test('plain properties', () => {
- toggleDeprecationWarning(true)
- Vue.prototype.$test = 1
- const vm = new Vue() as any
- expect(vm.$test).toBe(1)
- delete Vue.prototype.$test
- expect(
- deprecationData[DeprecationTypes.GLOBAL_MOUNT].message,
- ).toHaveBeenWarned()
- expect(
- deprecationData[DeprecationTypes.GLOBAL_PROTOTYPE].message,
- ).toHaveBeenWarned()
- })
- test('method this context', () => {
- Vue.prototype.$test = function () {
- return this.msg
- }
- const vm = new Vue({
- data() {
- return { msg: 'method' }
- },
- }) as any
- expect(vm.$test()).toBe('method')
- delete Vue.prototype.$test
- })
- test('defined properties', () => {
- Object.defineProperty(Vue.prototype, '$test', {
- configurable: true,
- get() {
- return this.msg
- },
- })
- const vm = new Vue({
- data() {
- return { msg: 'getter' }
- },
- }) as any
- expect(vm.$test).toBe('getter')
- delete Vue.prototype.$test
- })
- test('functions keeps additional properties', () => {
- function test(this: any) {
- return this.msg
- }
- test.additionalFn = () => {
- return 'additional fn'
- }
- Vue.prototype.$test = test
- const vm = new Vue({
- data() {
- return {
- msg: 'test',
- }
- },
- }) as any
- expect(typeof vm.$test).toBe('function')
- expect(typeof vm.$test.additionalFn).toBe('function')
- expect(vm.$test.additionalFn()).toBe('additional fn')
- delete Vue.prototype.$test
- })
- test('extended prototype', async () => {
- const Foo = Vue.extend()
- Foo.prototype.$test = 1
- const vm = new Foo() as any
- expect(vm.$test).toBe(1)
- const plain = new Vue() as any
- expect(plain.$test).toBeUndefined()
- })
- test('should affect apps created via createApp()', () => {
- Vue.prototype.$test = 1
- const vm = createApp({
- template: 'foo',
- }).mount(document.createElement('div')) as any
- expect(vm.$test).toBe(1)
- delete Vue.prototype.$test
- })
- })
- describe('GLOBAL_SET/DELETE', () => {
- test('set', () => {
- toggleDeprecationWarning(true)
- const obj: any = {}
- Vue.set(obj, 'foo', 1)
- expect(obj.foo).toBe(1)
- expect(
- deprecationData[DeprecationTypes.GLOBAL_SET].message,
- ).toHaveBeenWarned()
- })
- test('delete', () => {
- toggleDeprecationWarning(true)
- const obj: any = { foo: 1 }
- Vue.delete(obj, 'foo')
- expect('foo' in obj).toBe(false)
- expect(
- deprecationData[DeprecationTypes.GLOBAL_DELETE].message,
- ).toHaveBeenWarned()
- })
- })
- describe('GLOBAL_OBSERVABLE', () => {
- test('should work', () => {
- toggleDeprecationWarning(true)
- const obj = Vue.observable({})
- expect(isReactive(obj)).toBe(true)
- expect(
- deprecationData[DeprecationTypes.GLOBAL_OBSERVABLE].message,
- ).toHaveBeenWarned()
- })
- })
- describe('GLOBAL_PRIVATE_UTIL', () => {
- test('defineReactive', () => {
- toggleDeprecationWarning(true)
- const obj: any = {}
- Vue.util.defineReactive(obj, 'test', 1)
- let n
- effect(() => {
- n = obj.test
- })
- expect(n).toBe(1)
- obj.test++
- expect(n).toBe(2)
- expect(
- deprecationData[DeprecationTypes.GLOBAL_PRIVATE_UTIL].message,
- ).toHaveBeenWarned()
- })
- test('defineReactive on instance', async () => {
- const vm = new Vue({
- beforeCreate() {
- Vue.util.defineReactive(this, 'foo', 1)
- },
- template: `<div>{{ foo }}</div>`,
- }).$mount() as any
- expect(vm.$el).toBeInstanceOf(HTMLDivElement)
- expect(vm.$el.textContent).toBe('1')
- vm.foo = 2
- await nextTick()
- expect(vm.$el.textContent).toBe('2')
- })
- test('defineReactive on instance with key that starts with $', async () => {
- const vm = new Vue({
- beforeCreate() {
- Vue.util.defineReactive(this, '$foo', 1)
- },
- template: `<div>{{ $foo }}</div>`,
- }).$mount() as any
- expect(vm.$el.textContent).toBe('1')
- vm.$foo = 2
- await nextTick()
- expect(vm.$el.textContent).toBe('2')
- })
- test('defineReactive with object value', () => {
- const obj: any = {}
- const val = { a: 1 }
- Vue.util.defineReactive(obj, 'foo', val)
- let n
- effect(() => {
- n = obj.foo.a
- })
- expect(n).toBe(1)
- // mutating original
- val.a++
- expect(n).toBe(2)
- })
- test('defineReactive with array value', () => {
- const obj: any = {}
- const val = [1]
- Vue.util.defineReactive(obj, 'foo', val)
- let n
- effect(() => {
- n = obj.foo.length
- })
- expect(n).toBe(1)
- // mutating original
- val.push(2)
- expect(n).toBe(2)
- })
- })
- test('global asset registration should affect apps created via createApp', () => {
- Vue.component('foo', { template: 'foo' })
- const vm = createApp({
- template: '<foo/>',
- }).mount(document.createElement('div')) as any
- expect(vm.$el.textContent).toBe('foo')
- delete singletonApp._context.components.foo
- })
- test('post-facto global asset registration should affect apps created via createApp', () => {
- const app = createApp({
- template: '<foo/>',
- })
- Vue.component('foo', { template: 'foo' })
- const vm = app.mount(document.createElement('div'))
- expect(vm.$el.textContent).toBe('foo')
- delete singletonApp._context.components.foo
- })
- test('local asset registration should not affect other local apps', () => {
- const app1 = createApp({})
- const app2 = createApp({})
- app1.component('foo', {})
- app2.component('foo', {})
- expect(
- `Component "foo" has already been registered in target app`,
- ).not.toHaveBeenWarned()
- })
- test('local app-level mixin registration should not affect other local apps', () => {
- const app1 = createApp({ render: () => h('div') })
- const app2 = createApp({})
- const mixin = { created: vi.fn() }
- app1.mixin(mixin)
- app2.mixin(mixin)
- expect(`Mixin has already been applied`).not.toHaveBeenWarned()
- app1.mount(document.createElement('div'))
- expect(mixin.created).toHaveBeenCalledTimes(1)
- })
- // #5699
- test('local app config should not affect other local apps in v3 mode', () => {
- Vue.configureCompat({ MODE: 3 })
- const app1 = createApp({
- render: () => h('div'),
- provide() {
- return {
- test: 123,
- }
- },
- })
- app1.config.globalProperties.test = () => {}
- app1.mount(document.createElement('div'))
- const app2 = createApp({})
- expect(app2.config.globalProperties.test).toBe(undefined)
- })
|