vue3-yuanma/packages/vue-compat/__tests__/global.spec.ts

505 lines
12 KiB
TypeScript
Raw Normal View History

2021-04-10 06:52:14 +08:00
import Vue from '@vue/compat'
2021-04-28 05:34:19 +08:00
import { effect, isReactive } from '@vue/reactivity'
import { h, nextTick } from '@vue/runtime-core'
2021-04-28 05:34:19 +08:00
import {
DeprecationTypes,
deprecationData,
toggleDeprecationWarning
} from '../../runtime-core/src/compat/compatConfig'
import { singletonApp } from '../../runtime-core/src/compat/global'
import { createApp } from '../src/esm-index'
2021-04-10 06:52:14 +08:00
2021-04-28 05:34:19 +08:00
beforeEach(() => {
2021-04-29 00:29:51 +08:00
toggleDeprecationWarning(false)
2021-04-28 05:34:19 +08:00
Vue.configureCompat({ MODE: 2 })
})
afterEach(() => {
Vue.configureCompat({ MODE: 3 })
toggleDeprecationWarning(false)
})
describe('GLOBAL_MOUNT', () => {
test('new Vue() with el', () => {
toggleDeprecationWarning(true)
2021-04-10 11:51:50 +08:00
2021-04-10 06:52:14 +08:00
const el = document.createElement('div')
el.innerHTML = `{{ msg }}`
new Vue({
el,
2021-04-28 05:34:19 +08:00
compatConfig: { GLOBAL_MOUNT: true },
2021-04-10 06:52:14 +08:00
data() {
return {
msg: 'hello'
}
}
})
2021-04-28 05:34:19 +08:00
expect(
deprecationData[DeprecationTypes.GLOBAL_MOUNT].message
).toHaveBeenWarned()
2021-04-10 06:52:14 +08:00
expect(el.innerHTML).toBe('hello')
})
2021-04-28 05:34:19 +08:00
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.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', () => {
2021-07-20 06:24:18 +08:00
const a = jest.fn()
const b = jest.fn()
const c = jest.fn()
const d = jest.fn()
2021-04-28 05:34:19 +08:00
const A = Vue.extend({
created: a
2021-04-28 05:34:19 +08:00
})
const B = Vue.extend({
mixins: [A],
created: b
2021-04-28 05:34:19 +08:00
})
const C = Vue.extend({
extends: B,
created: c
2021-04-28 05:34:19 +08:00
})
const D = Vue.extend({
mixins: [C],
created: d,
2021-07-20 06:24:18 +08:00
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)
2021-04-28 05:34:19 +08:00
})
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.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', () => {
2021-07-20 06:24:18 +08:00
Vue.prototype.$test = function () {
2021-04-28 05:34:19 +08:00
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
})
2021-04-28 05:34:19 +08:00
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
})
2021-04-28 05:34:19 +08:00
})
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 = {}
// @ts-ignore
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()
})
2021-05-01 06:05:03 +08:00
test('defineReactive on instance', async () => {
const vm = new Vue({
beforeCreate() {
// @ts-ignore
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 on instance with key that starts with $', async () => {
const vm = new Vue({
beforeCreate() {
// @ts-ignore
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')
})
2021-05-01 06:05:03 +08:00
test('defineReactive with object value', () => {
const obj: any = {}
const val = { a: 1 }
// @ts-ignore
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]
// @ts-ignore
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)
})
2021-04-10 06:52:14 +08:00
})
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')) as any
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: jest.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)
})