refactor: rename packages

This commit is contained in:
Evan You
2018-10-26 15:44:50 -04:00
parent f142c322e0
commit e05673f4d3
81 changed files with 91 additions and 91 deletions

View 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)
})
})

View 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)
})
})

View 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)
})
})

View 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)
})
})

View 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)
})
})