refactor: rename packages
This commit is contained in:
218
packages/runtime-core/__tests__/attrsFallthrough.spec.ts
Normal file
218
packages/runtime-core/__tests__/attrsFallthrough.spec.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
// using DOM renderer because this case is mostly DOM-specific
|
||||
import { h, render, Component, nextTick, cloneVNode } from '@vue/runtime-dom'
|
||||
|
||||
describe('attribute fallthrough', () => {
|
||||
it('everything should be in props when component has no declared props', async () => {
|
||||
const click = jest.fn()
|
||||
const childUpdated = jest.fn()
|
||||
|
||||
class Hello extends Component {
|
||||
count: number = 0
|
||||
inc() {
|
||||
this.count++
|
||||
click()
|
||||
}
|
||||
render() {
|
||||
return h(Child, {
|
||||
foo: 1,
|
||||
id: 'test',
|
||||
class: 'c' + this.count,
|
||||
style: { color: this.count ? 'red' : 'green' },
|
||||
onClick: this.inc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Component<{ [key: string]: any }> {
|
||||
updated() {
|
||||
childUpdated()
|
||||
}
|
||||
render(props: any) {
|
||||
return cloneVNode(
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
class: 'c2',
|
||||
style: { fontWeight: 'bold' }
|
||||
},
|
||||
props.foo
|
||||
),
|
||||
props
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const root = document.createElement('div')
|
||||
document.body.appendChild(root)
|
||||
render(h(Hello), root)
|
||||
|
||||
const node = root.children[0] as HTMLElement
|
||||
|
||||
expect(node.getAttribute('id')).toBe('test')
|
||||
expect(node.getAttribute('foo')).toBe('1')
|
||||
expect(node.getAttribute('class')).toBe('c2 c0')
|
||||
expect(node.style.color).toBe('green')
|
||||
expect(node.style.fontWeight).toBe('bold')
|
||||
node.dispatchEvent(new CustomEvent('click'))
|
||||
expect(click).toHaveBeenCalled()
|
||||
|
||||
await nextTick()
|
||||
expect(childUpdated).toHaveBeenCalled()
|
||||
expect(node.getAttribute('id')).toBe('test')
|
||||
expect(node.getAttribute('foo')).toBe('1')
|
||||
expect(node.getAttribute('class')).toBe('c2 c1')
|
||||
expect(node.style.color).toBe('red')
|
||||
expect(node.style.fontWeight).toBe('bold')
|
||||
})
|
||||
|
||||
it('should separate in attrs when component has declared props', async () => {
|
||||
const click = jest.fn()
|
||||
const childUpdated = jest.fn()
|
||||
|
||||
class Hello extends Component {
|
||||
count = 0
|
||||
inc() {
|
||||
this.count++
|
||||
click()
|
||||
}
|
||||
render() {
|
||||
return h(Child, {
|
||||
foo: 123,
|
||||
id: 'test',
|
||||
class: 'c' + this.count,
|
||||
style: { color: this.count ? 'red' : 'green' },
|
||||
onClick: this.inc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Component<{ [key: string]: any; foo: number }> {
|
||||
static props = {
|
||||
foo: Number
|
||||
}
|
||||
updated() {
|
||||
childUpdated()
|
||||
}
|
||||
render() {
|
||||
return cloneVNode(
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
class: 'c2',
|
||||
style: { fontWeight: 'bold' }
|
||||
},
|
||||
this.$props.foo
|
||||
),
|
||||
this.$attrs
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const root = document.createElement('div')
|
||||
document.body.appendChild(root)
|
||||
render(h(Hello), root)
|
||||
|
||||
const node = root.children[0] as HTMLElement
|
||||
|
||||
// with declared props, any parent attr that isn't a prop falls through
|
||||
expect(node.getAttribute('id')).toBe('test')
|
||||
expect(node.getAttribute('class')).toBe('c2 c0')
|
||||
expect(node.style.color).toBe('green')
|
||||
expect(node.style.fontWeight).toBe('bold')
|
||||
node.dispatchEvent(new CustomEvent('click'))
|
||||
expect(click).toHaveBeenCalled()
|
||||
|
||||
// ...while declared ones remain props
|
||||
expect(node.hasAttribute('foo')).toBe(false)
|
||||
|
||||
await nextTick()
|
||||
expect(childUpdated).toHaveBeenCalled()
|
||||
expect(node.getAttribute('id')).toBe('test')
|
||||
expect(node.getAttribute('class')).toBe('c2 c1')
|
||||
expect(node.style.color).toBe('red')
|
||||
expect(node.style.fontWeight).toBe('bold')
|
||||
|
||||
expect(node.hasAttribute('foo')).toBe(false)
|
||||
})
|
||||
|
||||
it('should fallthrough on multi-nested components', async () => {
|
||||
const click = jest.fn()
|
||||
const childUpdated = jest.fn()
|
||||
const grandChildUpdated = jest.fn()
|
||||
|
||||
class Hello extends Component {
|
||||
count = 0
|
||||
inc() {
|
||||
this.count++
|
||||
click()
|
||||
}
|
||||
render() {
|
||||
return h(Child, {
|
||||
foo: 1,
|
||||
id: 'test',
|
||||
class: 'c' + this.count,
|
||||
style: { color: this.count ? 'red' : 'green' },
|
||||
onClick: this.inc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Component<{ [key: string]: any; foo: number }> {
|
||||
updated() {
|
||||
childUpdated()
|
||||
}
|
||||
render() {
|
||||
return h(GrandChild, this.$props)
|
||||
}
|
||||
}
|
||||
|
||||
class GrandChild extends Component<{ [key: string]: any; foo: number }> {
|
||||
static props = {
|
||||
foo: Number
|
||||
}
|
||||
updated() {
|
||||
grandChildUpdated()
|
||||
}
|
||||
render(props: any) {
|
||||
return cloneVNode(
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
class: 'c2',
|
||||
style: { fontWeight: 'bold' }
|
||||
},
|
||||
props.foo
|
||||
),
|
||||
this.$attrs
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const root = document.createElement('div')
|
||||
document.body.appendChild(root)
|
||||
render(h(Hello), root)
|
||||
|
||||
const node = root.children[0] as HTMLElement
|
||||
|
||||
// with declared props, any parent attr that isn't a prop falls through
|
||||
expect(node.getAttribute('id')).toBe('test')
|
||||
expect(node.getAttribute('class')).toBe('c2 c0')
|
||||
expect(node.style.color).toBe('green')
|
||||
expect(node.style.fontWeight).toBe('bold')
|
||||
node.dispatchEvent(new CustomEvent('click'))
|
||||
expect(click).toHaveBeenCalled()
|
||||
|
||||
// ...while declared ones remain props
|
||||
expect(node.hasAttribute('foo')).toBe(false)
|
||||
|
||||
await nextTick()
|
||||
expect(childUpdated).toHaveBeenCalled()
|
||||
expect(grandChildUpdated).toHaveBeenCalled()
|
||||
expect(node.getAttribute('id')).toBe('test')
|
||||
expect(node.getAttribute('class')).toBe('c2 c1')
|
||||
expect(node.style.color).toBe('red')
|
||||
expect(node.style.fontWeight).toBe('bold')
|
||||
|
||||
expect(node.hasAttribute('foo')).toBe(false)
|
||||
})
|
||||
})
|
||||
198
packages/runtime-core/__tests__/fragment.spec.ts
Normal file
198
packages/runtime-core/__tests__/fragment.spec.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import {
|
||||
h,
|
||||
Component,
|
||||
render,
|
||||
nodeOps,
|
||||
NodeTypes,
|
||||
TestElement,
|
||||
Fragment,
|
||||
observable,
|
||||
serialize,
|
||||
ChildrenFlags,
|
||||
nextTick,
|
||||
resetOps,
|
||||
dumpOps,
|
||||
NodeOpTypes
|
||||
} from '@vue/runtime-test'
|
||||
|
||||
describe('Fragments', () => {
|
||||
it('should allow returning multiple component root nodes', () => {
|
||||
class App extends Component {
|
||||
render() {
|
||||
return [h('div', 'one'), 'two']
|
||||
}
|
||||
}
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
expect(serialize(root)).toBe(`<div><div>one</div>two</div>`)
|
||||
expect(root.children.length).toBe(2)
|
||||
expect(root.children[0]).toMatchObject({
|
||||
type: NodeTypes.ELEMENT,
|
||||
tag: 'div'
|
||||
})
|
||||
expect((root.children[0] as TestElement).children[0]).toMatchObject({
|
||||
type: NodeTypes.TEXT,
|
||||
text: 'one'
|
||||
})
|
||||
expect(root.children[1]).toMatchObject({
|
||||
type: NodeTypes.TEXT,
|
||||
text: 'two'
|
||||
})
|
||||
})
|
||||
|
||||
it('should be able to explicitly create fragments', () => {
|
||||
class App extends Component {
|
||||
render() {
|
||||
return h('div', [h(Fragment, [h('div', 'one'), 'two'])])
|
||||
}
|
||||
}
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
const parent = root.children[0] as TestElement
|
||||
expect(serialize(parent)).toBe(`<div><div>one</div>two</div>`)
|
||||
})
|
||||
|
||||
it('should be able to patch fragment children (unkeyed)', async () => {
|
||||
const state = observable({ ok: true })
|
||||
class App extends Component {
|
||||
render() {
|
||||
return state.ok
|
||||
? h.f([h('div', 'one'), h.t('two')], ChildrenFlags.NONE_KEYED_VNODES)
|
||||
: h.f(
|
||||
[h('div', 'foo'), h.t('bar'), h.t('baz')],
|
||||
ChildrenFlags.NONE_KEYED_VNODES
|
||||
)
|
||||
}
|
||||
}
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
|
||||
expect(serialize(root)).toBe(`<div><div>one</div>two</div>`)
|
||||
|
||||
state.ok = false
|
||||
await nextTick()
|
||||
expect(serialize(root)).toBe(`<div><div>foo</div>barbaz</div>`)
|
||||
})
|
||||
|
||||
it('should be able to patch fragment children (implicitly keyed)', async () => {
|
||||
const state = observable({ ok: true })
|
||||
class App extends Component {
|
||||
render() {
|
||||
return state.ok
|
||||
? [h('div', 'one'), 'two']
|
||||
: [h('div', 'foo'), 'bar', 'baz']
|
||||
}
|
||||
}
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
|
||||
expect(serialize(root)).toBe(`<div><div>one</div>two</div>`)
|
||||
|
||||
state.ok = false
|
||||
await nextTick()
|
||||
expect(serialize(root)).toBe(`<div><div>foo</div>barbaz</div>`)
|
||||
})
|
||||
|
||||
it('should be able to patch fragment children (explcitly keyed)', async () => {
|
||||
const state = observable({ ok: true })
|
||||
class App extends Component {
|
||||
render() {
|
||||
return state.ok
|
||||
? [h('div', { key: 1 }, 'one'), h('div', { key: 2 }, 'two')]
|
||||
: [h('div', { key: 2 }, 'two'), h('div', { key: 1 }, 'one')]
|
||||
}
|
||||
}
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
|
||||
expect(serialize(root)).toBe(`<div><div>one</div><div>two</div></div>`)
|
||||
|
||||
resetOps()
|
||||
state.ok = false
|
||||
await nextTick()
|
||||
expect(serialize(root)).toBe(`<div><div>two</div><div>one</div></div>`)
|
||||
const ops = dumpOps()
|
||||
// should be moving nodes instead of re-creating them
|
||||
expect(ops.some(op => op.type === NodeOpTypes.CREATE)).toBe(false)
|
||||
})
|
||||
|
||||
it('should be able to move fragment', async () => {
|
||||
const state = observable({ ok: true })
|
||||
class App extends Component {
|
||||
render() {
|
||||
return state.ok
|
||||
? h('div', [
|
||||
h('div', { key: 1 }, 'outer'),
|
||||
h(Fragment, { key: 2 }, [
|
||||
h('div', { key: 1 }, 'one'),
|
||||
h('div', { key: 2 }, 'two')
|
||||
])
|
||||
])
|
||||
: h('div', [
|
||||
h(Fragment, { key: 2 }, [
|
||||
h('div', { key: 2 }, 'two'),
|
||||
h('div', { key: 1 }, 'one')
|
||||
]),
|
||||
h('div', { key: 1 }, 'outer')
|
||||
])
|
||||
}
|
||||
}
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
const parent = root.children[0] as TestElement
|
||||
|
||||
expect(serialize(parent)).toBe(
|
||||
`<div><div>outer</div><div>one</div><div>two</div></div>`
|
||||
)
|
||||
|
||||
resetOps()
|
||||
state.ok = false
|
||||
await nextTick()
|
||||
expect(serialize(parent)).toBe(
|
||||
`<div><div>two</div><div>one</div><div>outer</div></div>`
|
||||
)
|
||||
const ops = dumpOps()
|
||||
// should be moving nodes instead of re-creating them
|
||||
expect(ops.some(op => op.type === NodeOpTypes.CREATE)).toBe(false)
|
||||
})
|
||||
|
||||
it('should be able to handle nested fragments', async () => {
|
||||
const state = observable({ ok: true })
|
||||
class App extends Component {
|
||||
render() {
|
||||
return state.ok
|
||||
? [
|
||||
h('div', { key: 1 }, 'outer'),
|
||||
h(Fragment, { key: 2 }, [
|
||||
h('div', { key: 1 }, 'one'),
|
||||
h('div', { key: 2 }, 'two')
|
||||
])
|
||||
]
|
||||
: [
|
||||
h(Fragment, { key: 2 }, [
|
||||
h('div', { key: 2 }, 'two'),
|
||||
h('div', { key: 1 }, 'one')
|
||||
]),
|
||||
h('div', { key: 1 }, 'outer')
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
|
||||
expect(serialize(root)).toBe(
|
||||
`<div><div>outer</div><div>one</div><div>two</div></div>`
|
||||
)
|
||||
|
||||
resetOps()
|
||||
state.ok = false
|
||||
await nextTick()
|
||||
expect(serialize(root)).toBe(
|
||||
`<div><div>two</div><div>one</div><div>outer</div></div>`
|
||||
)
|
||||
const ops = dumpOps()
|
||||
// should be moving nodes instead of re-creating them
|
||||
expect(ops.some(op => op.type === NodeOpTypes.CREATE)).toBe(false)
|
||||
})
|
||||
})
|
||||
291
packages/runtime-core/__tests__/inheritance.spec.ts
Normal file
291
packages/runtime-core/__tests__/inheritance.spec.ts
Normal file
@@ -0,0 +1,291 @@
|
||||
import {
|
||||
Component,
|
||||
observable,
|
||||
h,
|
||||
nextTick,
|
||||
KeepAlive,
|
||||
ComponentPropsOptions,
|
||||
ComponentWatchOptions
|
||||
} from '@vue/runtime-core'
|
||||
import { createInstance, renderIntsance } from '@vue/runtime-test'
|
||||
|
||||
describe('class inheritance', () => {
|
||||
it('should merge data', () => {
|
||||
class Base extends Component {
|
||||
foo = 1
|
||||
data() {
|
||||
return {
|
||||
bar: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Base {
|
||||
foo: number
|
||||
bar: number
|
||||
baz: number
|
||||
qux: number = 4
|
||||
|
||||
data(): any {
|
||||
return {
|
||||
baz: 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const child = createInstance(Child)
|
||||
|
||||
expect(child.foo).toBe(1)
|
||||
expect(child.bar).toBe(2)
|
||||
expect(child.baz).toBe(3)
|
||||
expect(child.qux).toBe(4)
|
||||
})
|
||||
|
||||
it('should merge props', () => {
|
||||
class Base extends Component {
|
||||
static props: ComponentPropsOptions = {
|
||||
foo: Number
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Base {
|
||||
foo: number
|
||||
bar: number
|
||||
$props: { foo: number; bar: number }
|
||||
static props: ComponentPropsOptions = {
|
||||
bar: Number
|
||||
}
|
||||
}
|
||||
|
||||
const child = createInstance(Child, {
|
||||
foo: 1,
|
||||
bar: 2
|
||||
})
|
||||
|
||||
expect(child.foo).toBe(1)
|
||||
expect(child.bar).toBe(2)
|
||||
expect(child.$props.foo).toBe(1)
|
||||
expect(child.$props.bar).toBe(2)
|
||||
})
|
||||
|
||||
it('should merge lifecycle hooks', async () => {
|
||||
const calls: string[] = []
|
||||
const state = observable({ ok: true })
|
||||
|
||||
class Base extends Component {
|
||||
beforeCreate() {
|
||||
calls.push('base beforeCreate')
|
||||
}
|
||||
created() {
|
||||
calls.push('base created')
|
||||
}
|
||||
beforeMount() {
|
||||
calls.push('base beforeMount')
|
||||
}
|
||||
mounted() {
|
||||
calls.push('base mounted')
|
||||
}
|
||||
beforeUpdate() {
|
||||
calls.push('base beforeUpdate')
|
||||
}
|
||||
updated() {
|
||||
calls.push('base updated')
|
||||
}
|
||||
beforeUnmount() {
|
||||
calls.push('base beforeUnmount')
|
||||
}
|
||||
unmounted() {
|
||||
calls.push('base unmounted')
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Base {
|
||||
beforeCreate() {
|
||||
calls.push('child beforeCreate')
|
||||
}
|
||||
created() {
|
||||
calls.push('child created')
|
||||
}
|
||||
beforeMount() {
|
||||
calls.push('child beforeMount')
|
||||
}
|
||||
mounted() {
|
||||
calls.push('child mounted')
|
||||
}
|
||||
beforeUpdate() {
|
||||
calls.push('child beforeUpdate')
|
||||
}
|
||||
updated() {
|
||||
calls.push('child updated')
|
||||
}
|
||||
beforeUnmount() {
|
||||
calls.push('child beforeUnmount')
|
||||
}
|
||||
unmounted() {
|
||||
calls.push('child unmounted')
|
||||
}
|
||||
render() {
|
||||
return state.ok ? 'foo' : 'bar'
|
||||
}
|
||||
}
|
||||
|
||||
class Container extends Component {
|
||||
show = true
|
||||
render() {
|
||||
return this.show ? h(Child) : null
|
||||
}
|
||||
}
|
||||
|
||||
const container = renderIntsance(Container)
|
||||
expect(calls).toEqual([
|
||||
'base beforeCreate',
|
||||
'child beforeCreate',
|
||||
'base created',
|
||||
'child created',
|
||||
'base beforeMount',
|
||||
'child beforeMount',
|
||||
'base mounted',
|
||||
'child mounted'
|
||||
])
|
||||
|
||||
calls.length = 0
|
||||
state.ok = false
|
||||
await nextTick()
|
||||
expect(calls).toEqual([
|
||||
'base beforeUpdate',
|
||||
'child beforeUpdate',
|
||||
'base updated',
|
||||
'child updated'
|
||||
])
|
||||
|
||||
calls.length = 0
|
||||
container.show = false
|
||||
await nextTick()
|
||||
expect(calls).toEqual([
|
||||
'base beforeUnmount',
|
||||
'child beforeUnmount',
|
||||
'base unmounted',
|
||||
'child unmounted'
|
||||
])
|
||||
})
|
||||
|
||||
it('should merge lifecycle hooks (activated/deactivated)', async () => {
|
||||
const calls: string[] = []
|
||||
|
||||
class Base extends Component {
|
||||
activated() {
|
||||
calls.push('base activated')
|
||||
}
|
||||
deactivated() {
|
||||
calls.push('base deactivated')
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Base {
|
||||
activated() {
|
||||
calls.push('child activated')
|
||||
}
|
||||
deactivated() {
|
||||
calls.push('child deactivated')
|
||||
}
|
||||
render() {
|
||||
return 'foo'
|
||||
}
|
||||
}
|
||||
|
||||
class Container extends Component {
|
||||
ok = true
|
||||
render() {
|
||||
return h(KeepAlive, this.ok ? h(Child) : null)
|
||||
}
|
||||
}
|
||||
|
||||
const container = renderIntsance(Container)
|
||||
expect(container.$el.text).toBe('foo')
|
||||
|
||||
container.ok = false
|
||||
await nextTick()
|
||||
expect(container.$el.text).toBe('')
|
||||
expect(calls).toEqual(['base deactivated', 'child deactivated'])
|
||||
|
||||
calls.length = 0
|
||||
container.ok = true
|
||||
await nextTick()
|
||||
expect(container.$el.text).toBe('foo')
|
||||
expect(calls).toEqual(['base activated', 'child activated'])
|
||||
})
|
||||
|
||||
it('should merge watchers', async () => {
|
||||
const fooCallback = jest.fn()
|
||||
const barCallback = jest.fn()
|
||||
|
||||
class Base extends Component {
|
||||
static watch: ComponentWatchOptions = {
|
||||
foo: fooCallback
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Base {
|
||||
foo = 1
|
||||
bar = 2
|
||||
static watch: ComponentWatchOptions = {
|
||||
bar: barCallback
|
||||
}
|
||||
}
|
||||
|
||||
const child = createInstance(Child)
|
||||
child.foo = 2
|
||||
await nextTick()
|
||||
expect(fooCallback).toHaveBeenCalledWith(2, 1)
|
||||
child.bar = 3
|
||||
await nextTick()
|
||||
expect(barCallback).toHaveBeenCalledWith(3, 2)
|
||||
})
|
||||
|
||||
it('should inherit methods', () => {
|
||||
const fooCallback = jest.fn()
|
||||
const barCallback = jest.fn()
|
||||
|
||||
class Base extends Component {
|
||||
foo() {
|
||||
fooCallback()
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Base {
|
||||
bar() {
|
||||
barCallback()
|
||||
}
|
||||
}
|
||||
|
||||
const child = createInstance(Child)
|
||||
child.foo()
|
||||
child.bar()
|
||||
expect(fooCallback).toHaveBeenCalled()
|
||||
expect(barCallback).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should inherit computed properties', () => {
|
||||
class Base extends Component {
|
||||
a = 1
|
||||
get foo() {
|
||||
return this.a + 1
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Base {
|
||||
b = 1
|
||||
get bar() {
|
||||
return this.b + this.foo + 1
|
||||
}
|
||||
}
|
||||
|
||||
const child = createInstance(Child)
|
||||
expect(child.foo).toBe(2)
|
||||
expect(child.bar).toBe(4)
|
||||
|
||||
child.a = 2
|
||||
expect(child.foo).toBe(3)
|
||||
expect(child.bar).toBe(5)
|
||||
})
|
||||
})
|
||||
295
packages/runtime-core/__tests__/mixins.spec.ts
Normal file
295
packages/runtime-core/__tests__/mixins.spec.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
import { Component, ComponentClass, mixins } from '@vue/runtime-core'
|
||||
import { createInstance } from '@vue/runtime-test'
|
||||
|
||||
const calls: string[] = []
|
||||
|
||||
beforeEach(() => {
|
||||
calls.length = 0
|
||||
})
|
||||
|
||||
class ClassMixinA extends Component<{ p1: string }, { d11: number }> {
|
||||
// props
|
||||
static props = {
|
||||
p1: String
|
||||
}
|
||||
|
||||
// data
|
||||
d1 = 1
|
||||
data() {
|
||||
return {
|
||||
d11: 2
|
||||
}
|
||||
}
|
||||
|
||||
// computed
|
||||
get c1() {
|
||||
return this.d1 + this.d11
|
||||
}
|
||||
|
||||
// lifecycle
|
||||
created() {
|
||||
calls.push('created from mixin A')
|
||||
}
|
||||
|
||||
// methods
|
||||
foo() {
|
||||
return this.d1
|
||||
}
|
||||
}
|
||||
|
||||
class ClassMixinB extends Component<{ p2: string }, { d21: number }> {
|
||||
// props
|
||||
static props = {
|
||||
p2: String
|
||||
}
|
||||
|
||||
// data
|
||||
d2 = 1
|
||||
data() {
|
||||
return {
|
||||
d21: 2
|
||||
}
|
||||
}
|
||||
|
||||
get c2() {
|
||||
return this.d2 + this.d21
|
||||
}
|
||||
|
||||
// lifecycle
|
||||
created() {
|
||||
calls.push('created from mixin B')
|
||||
}
|
||||
|
||||
// methods
|
||||
bar() {
|
||||
return this.d2
|
||||
}
|
||||
}
|
||||
|
||||
const ObjectMixinA = {
|
||||
props: {
|
||||
p1: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
d1: 1,
|
||||
d11: 2
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
c1() {
|
||||
return this.d1 + this.d11
|
||||
}
|
||||
},
|
||||
created() {
|
||||
calls.push('created from mixin A')
|
||||
},
|
||||
methods: {
|
||||
foo() {
|
||||
return this.d1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ObjectMixinB = {
|
||||
props: {
|
||||
p2: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
d2: 1,
|
||||
d21: 2
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
c2() {
|
||||
return this.d2 + this.d21
|
||||
}
|
||||
},
|
||||
created() {
|
||||
calls.push('created from mixin B')
|
||||
},
|
||||
methods: {
|
||||
bar() {
|
||||
return this.d2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assertMixins(Test: any) {
|
||||
const instance = createInstance(Test, {
|
||||
p1: '1',
|
||||
p2: '2',
|
||||
p3: '3'
|
||||
}) as any
|
||||
|
||||
// data
|
||||
expect(instance.d1).toBe(1)
|
||||
expect(instance.d11).toBe(2)
|
||||
expect(instance.d2).toBe(1)
|
||||
expect(instance.d21).toBe(2)
|
||||
expect(instance.d3).toBe(1)
|
||||
expect(instance.d31).toBe(2)
|
||||
|
||||
// props
|
||||
expect(instance.p1).toBe('1')
|
||||
expect(instance.p2).toBe('2')
|
||||
expect(instance.p3).toBe('3')
|
||||
expect(instance.$props.p1).toBe('1')
|
||||
expect(instance.$props.p2).toBe('2')
|
||||
expect(instance.$props.p3).toBe('3')
|
||||
|
||||
// computed
|
||||
expect(instance.c1).toBe(3)
|
||||
expect(instance.c2).toBe(3)
|
||||
expect(instance.c3).toBe(3)
|
||||
|
||||
// lifecycle
|
||||
expect(calls).toEqual([
|
||||
'created from mixin A',
|
||||
'created from mixin B',
|
||||
'created from Test'
|
||||
])
|
||||
|
||||
// methods
|
||||
expect(instance.foo()).toBe(1)
|
||||
expect(instance.bar()).toBe(1)
|
||||
expect(instance.baz()).toBe(1)
|
||||
}
|
||||
|
||||
describe('mixins', () => {
|
||||
it('should work with classes', () => {
|
||||
class Test extends mixins(ClassMixinA, ClassMixinB)<
|
||||
{ p3: string },
|
||||
{ d31: number }
|
||||
> {
|
||||
static props = {
|
||||
p3: String
|
||||
}
|
||||
|
||||
d3 = 1
|
||||
data(): any {
|
||||
return {
|
||||
d31: 2
|
||||
}
|
||||
}
|
||||
|
||||
get c3() {
|
||||
return this.d3 + this.d31
|
||||
}
|
||||
|
||||
created() {
|
||||
calls.push('created from Test')
|
||||
}
|
||||
|
||||
baz() {
|
||||
return this.d3
|
||||
}
|
||||
}
|
||||
|
||||
const instance = createInstance(Test, {
|
||||
p1: '1',
|
||||
p2: '2',
|
||||
p3: '3'
|
||||
})
|
||||
|
||||
// we duplicate the assertions because they serve as type tests as well
|
||||
|
||||
// data
|
||||
expect(instance.d1).toBe(1)
|
||||
expect(instance.d11).toBe(2)
|
||||
expect(instance.d2).toBe(1)
|
||||
expect(instance.d21).toBe(2)
|
||||
expect(instance.d3).toBe(1)
|
||||
expect(instance.d31).toBe(2)
|
||||
|
||||
// props
|
||||
expect(instance.p1).toBe('1')
|
||||
expect(instance.p2).toBe('2')
|
||||
expect(instance.p3).toBe('3')
|
||||
expect(instance.$props.p1).toBe('1')
|
||||
expect(instance.$props.p2).toBe('2')
|
||||
expect(instance.$props.p3).toBe('3')
|
||||
|
||||
// computed
|
||||
expect(instance.c1).toBe(3)
|
||||
expect(instance.c2).toBe(3)
|
||||
expect(instance.c3).toBe(3)
|
||||
|
||||
// lifecycle
|
||||
expect(calls).toEqual([
|
||||
'created from mixin A',
|
||||
'created from mixin B',
|
||||
'created from Test'
|
||||
])
|
||||
|
||||
// methods
|
||||
expect(instance.foo()).toBe(1)
|
||||
expect(instance.bar()).toBe(1)
|
||||
expect(instance.baz()).toBe(1)
|
||||
})
|
||||
|
||||
it('should work with objects', () => {
|
||||
class Test extends ((mixins as any)(
|
||||
ObjectMixinA,
|
||||
ObjectMixinB
|
||||
) as ComponentClass)<{ p3: string }, { d31: number }> {
|
||||
static props = {
|
||||
p3: String
|
||||
}
|
||||
|
||||
d3 = 1
|
||||
data(): any {
|
||||
return {
|
||||
d31: 2
|
||||
}
|
||||
}
|
||||
|
||||
get c3() {
|
||||
return this.d3 + this.d31
|
||||
}
|
||||
|
||||
created() {
|
||||
calls.push('created from Test')
|
||||
}
|
||||
|
||||
baz() {
|
||||
return this.d3
|
||||
}
|
||||
}
|
||||
|
||||
assertMixins(Test)
|
||||
})
|
||||
|
||||
it('should work with a mix of objects and classes', () => {
|
||||
class Test extends ((mixins as any)(
|
||||
ClassMixinA,
|
||||
ObjectMixinB
|
||||
) as ComponentClass)<{ p3: string }, { d31: number }> {
|
||||
static props = {
|
||||
p3: String
|
||||
}
|
||||
|
||||
d3 = 1
|
||||
data(): any {
|
||||
return {
|
||||
d31: 2
|
||||
}
|
||||
}
|
||||
|
||||
get c3() {
|
||||
return this.d3 + this.d31
|
||||
}
|
||||
|
||||
created() {
|
||||
calls.push('created from Test')
|
||||
}
|
||||
|
||||
baz() {
|
||||
return this.d3
|
||||
}
|
||||
}
|
||||
|
||||
assertMixins(Test)
|
||||
})
|
||||
})
|
||||
121
packages/runtime-core/__tests__/parentChain.spec.ts
Normal file
121
packages/runtime-core/__tests__/parentChain.spec.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import {
|
||||
h,
|
||||
Component,
|
||||
render,
|
||||
nodeOps,
|
||||
observable,
|
||||
nextTick
|
||||
} from '@vue/runtime-test'
|
||||
|
||||
describe('Parent chain management', () => {
|
||||
it('should have correct $parent / $root / $children', async () => {
|
||||
let child: any
|
||||
let grandChildren: any[] = []
|
||||
|
||||
const state = observable({ ok: true })
|
||||
|
||||
class Parent extends Component {
|
||||
render() {
|
||||
return h(Child)
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Component {
|
||||
created() {
|
||||
child = this
|
||||
}
|
||||
render() {
|
||||
return [state.ok ? h(GrandChild) : null, h(GrandChild)]
|
||||
}
|
||||
}
|
||||
|
||||
class GrandChild extends Component {
|
||||
created() {
|
||||
grandChildren.push(this)
|
||||
}
|
||||
unmounted() {
|
||||
grandChildren.splice(grandChildren.indexOf(this), 1)
|
||||
}
|
||||
render() {}
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
const parent = render(h(Parent), root) as Component
|
||||
|
||||
expect(child.$parent).toBe(parent)
|
||||
expect(child.$root).toBe(parent)
|
||||
|
||||
grandChildren.forEach(grandChild => {
|
||||
expect(grandChild.$parent).toBe(child)
|
||||
expect(grandChild.$root).toBe(parent)
|
||||
})
|
||||
|
||||
expect(parent.$children).toEqual([child])
|
||||
expect(grandChildren.length).toBe(2)
|
||||
expect(child.$children).toEqual(grandChildren)
|
||||
|
||||
state.ok = false
|
||||
await nextTick()
|
||||
expect(grandChildren.length).toBe(1)
|
||||
expect(child.$children).toEqual(grandChildren)
|
||||
})
|
||||
|
||||
it('should have correct $parent / $root w/ functional component in between', async () => {
|
||||
let child: any
|
||||
let grandChildren: any[] = []
|
||||
|
||||
const state = observable({ ok: true })
|
||||
|
||||
class Parent extends Component {
|
||||
render() {
|
||||
return h(FunctionalChild)
|
||||
}
|
||||
}
|
||||
|
||||
const FunctionalChild = () => h(Child)
|
||||
|
||||
class Child extends Component {
|
||||
created() {
|
||||
child = this
|
||||
}
|
||||
render() {
|
||||
return [
|
||||
state.ok ? h(FunctionalGrandChild) : null,
|
||||
h(FunctionalGrandChild)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const FunctionalGrandChild = () => h(GrandChild)
|
||||
|
||||
class GrandChild extends Component {
|
||||
created() {
|
||||
grandChildren.push(this)
|
||||
}
|
||||
unmounted() {
|
||||
grandChildren.splice(grandChildren.indexOf(this), 1)
|
||||
}
|
||||
render() {}
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
const parent = render(h(Parent), root) as Component
|
||||
|
||||
expect(child.$parent).toBe(parent)
|
||||
expect(child.$root).toBe(parent)
|
||||
|
||||
grandChildren.forEach(grandChild => {
|
||||
expect(grandChild.$parent).toBe(child)
|
||||
expect(grandChild.$root).toBe(parent)
|
||||
})
|
||||
|
||||
expect(parent.$children).toEqual([child])
|
||||
expect(grandChildren.length).toBe(2)
|
||||
expect(child.$children).toEqual(grandChildren)
|
||||
|
||||
state.ok = false
|
||||
await nextTick()
|
||||
expect(grandChildren.length).toBe(1)
|
||||
expect(child.$children).toEqual(grandChildren)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user