From 645c1eceea36aaad7bb24a82e769889f2f0eae58 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 20 Jun 2019 21:28:37 +0800 Subject: [PATCH] wip: update test runtime --- packages/runtime-dom/src/index.ts | 11 +- .../src/{rendererOptions.ts => nodeOps.ts} | 8 +- .../__tests__/testRuntime.spec.ts | 280 +++++++++--------- packages/runtime-test/src/index.ts | 37 +-- packages/runtime-test/src/nodeOps.ts | 136 +++++---- .../src/{patchData.ts => patchProp.ts} | 6 +- packages/runtime-test/src/serialize.ts | 19 +- 7 files changed, 245 insertions(+), 252 deletions(-) rename packages/runtime-dom/src/{rendererOptions.ts => nodeOps.ts} (84%) rename packages/runtime-test/src/{patchData.ts => patchProp.ts} (87%) diff --git a/packages/runtime-dom/src/index.ts b/packages/runtime-dom/src/index.ts index dd9d7c31..e687f1da 100644 --- a/packages/runtime-dom/src/index.ts +++ b/packages/runtime-dom/src/index.ts @@ -1,10 +1,11 @@ import { createRenderer, VNode } from '@vue/runtime-core' -import { DOMRendererOptions } from './rendererOptions' +import { nodeOps } from './nodeOps' +import { patchProp } from './patchProp' -export const render = createRenderer(DOMRendererOptions) as ( - vnode: VNode | null, - container: HTMLElement -) => VNode +export const render = createRenderer({ + patchProp, + ...nodeOps +}) as (vnode: VNode | null, container: HTMLElement) => VNode // re-export everything from core // h, Component, observer API, nextTick, flags & types diff --git a/packages/runtime-dom/src/rendererOptions.ts b/packages/runtime-dom/src/nodeOps.ts similarity index 84% rename from packages/runtime-dom/src/rendererOptions.ts rename to packages/runtime-dom/src/nodeOps.ts index af2325d0..9316571f 100644 --- a/packages/runtime-dom/src/rendererOptions.ts +++ b/packages/runtime-dom/src/nodeOps.ts @@ -1,12 +1,7 @@ -import { RendererOptions } from '@vue/runtime-core' -import { patchProp } from './patchProp' - const doc = document const svgNS = 'http://www.w3.org/2000/svg' -export const DOMRendererOptions: RendererOptions = { - patchProp, - +export const nodeOps = { insert: (child: Node, parent: Node, anchor?: Node) => { if (anchor != null) { parent.insertBefore(child, anchor) @@ -16,7 +11,6 @@ export const DOMRendererOptions: RendererOptions = { }, remove: (child: Node) => { - if (!child) debugger const parent = child.parentNode if (parent != null) { parent.removeChild(child) diff --git a/packages/runtime-test/__tests__/testRuntime.spec.ts b/packages/runtime-test/__tests__/testRuntime.spec.ts index ad7c421e..ce0b8c83 100644 --- a/packages/runtime-test/__tests__/testRuntime.spec.ts +++ b/packages/runtime-test/__tests__/testRuntime.spec.ts @@ -1,36 +1,32 @@ import { - h, + createVNode as h, render, - Component, nodeOps, NodeTypes, TestElement, - TestText, - dumpOps, - NodeOpTypes, - nextTick, - observable, - resetOps, - serialize, - renderInstance, - triggerEvent + TestText + // dumpOps, + // NodeOpTypes, + // nextTick, + // state, + // resetOps, + // serialize, + // triggerEvent } from '../src' describe('test renderer', () => { it('should work', async () => { - class App extends Component { - render() { - return h( - 'div', - { - id: 'test' - }, - 'hello' - ) - } - } const root = nodeOps.createElement('div') - await render(h(App), root) + render( + h( + 'div', + { + id: 'test' + }, + 'hello' + ), + root + ) expect(root.children.length).toBe(1) @@ -44,137 +40,137 @@ describe('test renderer', () => { expect(text.text).toBe('hello') }) - it('should record ops', async () => { - const state = observable({ - id: 'test', - text: 'hello' - }) + // it('should record ops', async () => { + // const store = state({ + // id: 'test', + // text: 'hello' + // }) - class App extends Component { - render() { - return h( - 'div', - { - id: state.id - }, - state.text - ) - } - } - const root = nodeOps.createElement('div') + // class App extends Component { + // render() { + // return h( + // 'div', + // { + // id: store.id + // }, + // store.text + // ) + // } + // } + // const root = nodeOps.createElement('div') - resetOps() - await render(h(App), root) - const ops = dumpOps() + // resetOps() + // await render(h(App), root) + // const ops = dumpOps() - expect(ops.length).toBe(5) + // expect(ops.length).toBe(5) - expect(ops[0]).toEqual({ - type: NodeOpTypes.CREATE, - nodeType: NodeTypes.ELEMENT, - tag: 'div', - targetNode: root.children[0] - }) + // expect(ops[0]).toEqual({ + // type: NodeOpTypes.CREATE, + // nodeType: NodeTypes.ELEMENT, + // tag: 'div', + // targetNode: root.children[0] + // }) - expect(ops[1]).toEqual({ - type: NodeOpTypes.PATCH, - targetNode: root.children[0], - propKey: 'id', - propPrevValue: null, - propNextValue: 'test' - }) + // expect(ops[1]).toEqual({ + // type: NodeOpTypes.PATCH, + // targetNode: root.children[0], + // propKey: 'id', + // propPrevValue: null, + // propNextValue: 'test' + // }) - expect(ops[2]).toEqual({ - type: NodeOpTypes.CREATE, - nodeType: NodeTypes.TEXT, - text: 'hello', - targetNode: (root.children[0] as TestElement).children[0] - }) + // expect(ops[2]).toEqual({ + // type: NodeOpTypes.CREATE, + // nodeType: NodeTypes.TEXT, + // text: 'hello', + // targetNode: (root.children[0] as TestElement).children[0] + // }) - expect(ops[3]).toEqual({ - type: NodeOpTypes.APPEND, - targetNode: (root.children[0] as TestElement).children[0], - parentNode: root.children[0] - }) + // expect(ops[3]).toEqual({ + // type: NodeOpTypes.APPEND, + // targetNode: (root.children[0] as TestElement).children[0], + // parentNode: root.children[0] + // }) - expect(ops[4]).toEqual({ - type: NodeOpTypes.APPEND, - targetNode: root.children[0], - parentNode: root - }) + // expect(ops[4]).toEqual({ + // type: NodeOpTypes.APPEND, + // targetNode: root.children[0], + // parentNode: root + // }) - // test update ops - state.id = 'foo' - state.text = 'bar' - await nextTick() + // // test update ops + // store.id = 'foo' + // store.text = 'bar' + // await nextTick() - const updateOps = dumpOps() - expect(updateOps.length).toBe(2) + // const updateOps = dumpOps() + // expect(updateOps.length).toBe(2) - expect(updateOps[0]).toEqual({ - type: NodeOpTypes.PATCH, - targetNode: root.children[0], - propKey: 'id', - propPrevValue: 'test', - propNextValue: 'foo' - }) + // expect(updateOps[0]).toEqual({ + // type: NodeOpTypes.PATCH, + // targetNode: root.children[0], + // propKey: 'id', + // propPrevValue: 'test', + // propNextValue: 'foo' + // }) - expect(updateOps[1]).toEqual({ - type: NodeOpTypes.SET_TEXT, - targetNode: (root.children[0] as TestElement).children[0], - text: 'bar' - }) - }) + // expect(updateOps[1]).toEqual({ + // type: NodeOpTypes.SET_TEXT, + // targetNode: (root.children[0] as TestElement).children[0], + // text: 'bar' + // }) + // }) - it('should be able to serialize nodes', async () => { - class App extends Component { - render() { - return h( - 'div', - { - id: 'test' - }, - [h('span', 'foo'), 'hello'] - ) - } - } - const root = nodeOps.createElement('div') - await render(h(App), root) - expect(serialize(root)).toEqual( - `
foohello
` - ) - expect(serialize(root, 2)).toEqual( - `
-
- - foo - - hello -
-
` - ) - }) + // it('should be able to serialize nodes', async () => { + // class App extends Component { + // render() { + // return h( + // 'div', + // { + // id: 'test' + // }, + // [h('span', 'foo'), 'hello'] + // ) + // } + // } + // const root = nodeOps.createElement('div') + // await render(h(App), root) + // expect(serialize(root)).toEqual( + // `
foohello
` + // ) + // expect(serialize(root, 2)).toEqual( + // `
+ //
+ // + // foo + // + // hello + //
+ //
` + // ) + // }) - it('should be able to trigger events', async () => { - class App extends Component { - count = 0 - inc() { - this.count++ - } - render() { - return h( - 'div', - { - onClick: this.inc - }, - this.count - ) - } - } - const app = await renderInstance(App) - triggerEvent(app.$el, 'click') - expect(app.count).toBe(1) - await nextTick() - expect(serialize(app.$el)).toBe(`
1
`) - }) + // it('should be able to trigger events', async () => { + // class App extends Component { + // count = 0 + // inc() { + // this.count++ + // } + // render() { + // return h( + // 'div', + // { + // onClick: this.inc + // }, + // this.count + // ) + // } + // } + // const app = await renderInstance(App) + // triggerEvent(app.$el, 'click') + // expect(app.count).toBe(1) + // await nextTick() + // expect(serialize(app.$el)).toBe(`
1
`) + // }) }) diff --git a/packages/runtime-test/src/index.ts b/packages/runtime-test/src/index.ts index 5e1d35a9..db8d0cad 100644 --- a/packages/runtime-test/src/index.ts +++ b/packages/runtime-test/src/index.ts @@ -1,36 +1,11 @@ -import { - h, - createRenderer, - Component, - createComponentInstance -} from '@vue/runtime-core' +import { createRenderer, VNode } from '@vue/runtime-core' import { nodeOps, TestElement } from './nodeOps' -import { patchData } from './patchData' +import { patchProp } from './patchProp' -const { render: _render } = createRenderer({ - nodeOps, - patchData -}) - -type publicRender = ( - node: {} | null, - container: TestElement -) => Promise -export const render = _render as publicRender - -export function createInstance( - Class: new () => T, - props?: any -): T { - return createComponentInstance(h(Class, props)).$proxy as any -} - -export function renderInstance( - Class: new () => T, - props?: any -): Promise { - return render(h(Class, props), nodeOps.createElement('div')) as any -} +export const render = createRenderer({ + patchProp, + ...nodeOps +}) as (node: VNode | null, container: TestElement) => VNode export { serialize } from './serialize' export { triggerEvent } from './triggerEvent' diff --git a/packages/runtime-test/src/nodeOps.ts b/packages/runtime-test/src/nodeOps.ts index 9b4e4846..926b3618 100644 --- a/packages/runtime-test/src/nodeOps.ts +++ b/packages/runtime-test/src/nodeOps.ts @@ -1,6 +1,7 @@ export const enum NodeTypes { TEXT = 'text', - ELEMENT = 'element' + ELEMENT = 'element', + COMMENT = 'comment' } export interface TestElement { @@ -20,15 +21,21 @@ export interface TestText { text: string } -export type TestNode = TestElement | TestText +export interface TestComment { + id: number + type: NodeTypes.COMMENT + parentNode: TestElement | null + text: string +} + +export type TestNode = TestElement | TestText | TestComment export const enum NodeOpTypes { CREATE = 'create', INSERT = 'insert', - APPEND = 'append', REMOVE = 'remove', SET_TEXT = 'setText', - CLEAR = 'clearContent', + SET_ELEMENT_TEXT = 'setElementText', PATCH = 'patch' } @@ -39,7 +46,7 @@ export interface NodeOp { text?: string targetNode?: TestNode parentNode?: TestElement - refNode?: TestNode + refNode?: TestNode | null propKey?: string propPrevValue?: any propNextValue?: any @@ -97,6 +104,22 @@ function createText(text: string): TestText { return node } +function createComment(text: string): TestComment { + const node: TestComment = { + id: nodeId++, + type: NodeTypes.COMMENT, + text, + parentNode: null + } + logNodeOp({ + type: NodeOpTypes.CREATE, + nodeType: NodeTypes.COMMENT, + targetNode: node, + text + }) + return node +} + function setText(node: TestText, text: string) { logNodeOp({ type: NodeOpTypes.SET_TEXT, @@ -106,28 +129,15 @@ function setText(node: TestText, text: string) { node.text = text } -function appendChild(parent: TestElement, child: TestNode) { - logNodeOp({ - type: NodeOpTypes.APPEND, - targetNode: child, - parentNode: parent - }) - if (child.parentNode) { - removeChild(child.parentNode, child) - } - parent.children.push(child) - child.parentNode = parent -} - -function insertBefore(parent: TestElement, child: TestNode, ref: TestNode) { - if (child.parentNode) { - removeChild(child.parentNode, child) - } - const refIndex = parent.children.indexOf(ref) - if (refIndex === -1) { - console.error('ref: ', ref) - console.error('parent: ', parent) - throw new Error('ref is not a child of parent') +function insert(child: TestNode, parent: TestElement, ref?: TestNode | null) { + let refIndex + if (ref != null) { + refIndex = parent.children.indexOf(ref) + if (refIndex === -1) { + console.error('ref: ', ref) + console.error('parent: ', parent) + throw new Error('ref is not a child of parent') + } } logNodeOp({ type: NodeOpTypes.INSERT, @@ -135,42 +145,48 @@ function insertBefore(parent: TestElement, child: TestNode, ref: TestNode) { parentNode: parent, refNode: ref }) - parent.children.splice(refIndex, 0, child) - child.parentNode = parent -} - -function removeChild(parent: TestElement, child: TestNode) { - logNodeOp({ - type: NodeOpTypes.REMOVE, - targetNode: child, - parentNode: parent - }) - const i = parent.children.indexOf(child) - if (i > -1) { - parent.children.splice(i, 1) + remove(child) + if (refIndex === undefined) { + parent.children.push(child) + child.parentNode = parent } else { - console.error('target: ', child) - console.error('parent: ', parent) - throw Error('target is not a childNode of parent') + parent.children.splice(refIndex, 0, child) + child.parentNode = parent } - child.parentNode = null } -function clearContent(node: TestNode) { - logNodeOp({ - type: NodeOpTypes.CLEAR, - targetNode: node - }) - if (node.type === NodeTypes.ELEMENT) { - node.children.forEach(c => { - c.parentNode = null +function remove(child: TestNode) { + const parent = child.parentNode + if (parent != null) { + logNodeOp({ + type: NodeOpTypes.REMOVE, + targetNode: child, + parentNode: parent }) - node.children = [] - } else { - node.text = '' + const i = parent.children.indexOf(child) + if (i > -1) { + parent.children.splice(i, 1) + } else { + console.error('target: ', child) + console.error('parent: ', parent) + throw Error('target is not a childNode of parent') + } + child.parentNode = null } } +function setElementText(el: TestElement, text: string) { + logNodeOp({ + type: NodeOpTypes.SET_ELEMENT_TEXT, + targetNode: el, + text + }) + el.children.forEach(c => { + c.parentNode = null + }) + el.children = [createText(text)] +} + function parentNode(node: TestNode): TestElement | null { return node.parentNode } @@ -189,16 +205,14 @@ function querySelector() { } export const nodeOps = { + insert, + remove, createElement, createText, + createComment, setText, - appendChild, - insertBefore, - removeChild, - clearContent, + setElementText, parentNode, nextSibling, querySelector } - -export function patchData() {} diff --git a/packages/runtime-test/src/patchData.ts b/packages/runtime-test/src/patchProp.ts similarity index 87% rename from packages/runtime-test/src/patchData.ts rename to packages/runtime-test/src/patchProp.ts index c46f82a0..381d73b2 100644 --- a/packages/runtime-test/src/patchData.ts +++ b/packages/runtime-test/src/patchProp.ts @@ -1,11 +1,11 @@ import { TestElement, logNodeOp, NodeOpTypes } from './nodeOps' import { isOn } from '@vue/shared' -export function patchData( +export function patchProp( el: TestElement, key: string, - prevValue: any, - nextValue: any + nextValue: any, + prevValue: any ) { logNodeOp({ type: NodeOpTypes.PATCH, diff --git a/packages/runtime-test/src/serialize.ts b/packages/runtime-test/src/serialize.ts index c4ec82ea..fa401f4a 100644 --- a/packages/runtime-test/src/serialize.ts +++ b/packages/runtime-test/src/serialize.ts @@ -1,4 +1,10 @@ -import { TestElement, TestNode, NodeTypes, TestText } from './nodeOps' +import { + TestElement, + TestNode, + NodeTypes, + TestText, + TestComment +} from './nodeOps' import { isOn } from '@vue/shared' export function serialize( @@ -37,7 +43,14 @@ function serializeElement( ) } -function serializeText(node: TestText, indent: number, depth: number): string { +function serializeText( + node: TestText | TestComment, + indent: number, + depth: number +): string { const padding = indent ? ` `.repeat(indent).repeat(depth) : `` - return padding + node.text + return ( + padding + + (node.type === NodeTypes.COMMENT ? `` : node.text) + ) }