test(compiler): tests for ast transform

This commit is contained in:
Evan You 2019-09-19 12:20:59 -04:00
parent 10c1a2b332
commit 5f49018601
3 changed files with 135 additions and 4 deletions

View File

@ -12,7 +12,7 @@ import {
AttributeNode AttributeNode
} from '../src/ast' } from '../src/ast'
describe('base parser', () => { describe('compiler: parse', () => {
describe('Text', () => { describe('Text', () => {
test('simple text', () => { test('simple text', () => {
const ast = parse('some text') const ast = parse('some text')

View File

@ -0,0 +1,128 @@
import { parse } from '../src/parse'
import { transform, Transform } from '../src/transform'
import { ElementNode, NodeTypes } from '../src/ast'
import { ErrorCodes, createCompilerError } from '../src/errors'
describe('compiler: transform', () => {
test('context state', () => {
const ast = parse(`<div>hello {{ world }}</div>`)
// manually store call arguments because context is mutable and shared
// across calls
const calls: any[] = []
const plugin: Transform = (node, context) => {
calls.push([node, Object.assign({}, context)])
}
transform(ast, {
transforms: [plugin]
})
const div = ast.children[0] as ElementNode
expect(calls.length).toBe(3)
expect(calls[0]).toMatchObject([
div,
{
parent: ast,
ancestors: [ast],
childIndex: 0
}
])
expect(calls[1]).toMatchObject([
div.children[0],
{
parent: div,
ancestors: [ast, div],
childIndex: 0
}
])
expect(calls[2]).toMatchObject([
div.children[1],
{
parent: div,
ancestors: [ast, div],
childIndex: 1
}
])
})
test('context.replaceNode', () => {
const ast = parse(`<div/><span/>`)
const plugin: Transform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
// change the node to <p>
context.replaceNode(
Object.assign({}, node, {
tag: 'p',
children: [
{
type: NodeTypes.TEXT,
content: 'hello',
isEmpty: false
}
]
})
)
}
}
const spy = jest.fn(plugin)
transform(ast, {
transforms: [spy]
})
expect(ast.children.length).toBe(2)
const newElement = ast.children[0] as ElementNode
expect(newElement.tag).toBe('p')
expect(spy).toHaveBeenCalledTimes(3)
// should traverse the children of replaced node
expect(spy.mock.calls[1][0]).toBe(newElement.children[0])
// should traverse the node after the replaced node
expect(spy.mock.calls[2][0]).toBe(ast.children[1])
})
test('context.removeNode', () => {
const ast = parse(`<span/><div/><span/>`)
const c1 = ast.children[0]
const c2 = ast.children[2]
const plugin: Transform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
context.removeNode()
}
}
const spy = jest.fn(plugin)
transform(ast, {
transforms: [spy]
})
expect(ast.children.length).toBe(2)
expect(ast.children[0]).toBe(c1)
expect(ast.children[1]).toBe(c2)
expect(spy).toHaveBeenCalledTimes(3)
// should traverse nodes around removed
expect(spy.mock.calls[0][0]).toBe(c1)
expect(spy.mock.calls[2][0]).toBe(c2)
})
test('onError option', () => {
const ast = parse(`<div/>`)
const loc = ast.children[0].loc.start
const plugin: Transform = (node, context) => {
context.onError(
createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc.start)
)
}
const spy = jest.fn()
transform(ast, {
transforms: [plugin],
onError: spy
})
expect(spy.mock.calls[0]).toMatchObject([
{
code: ErrorCodes.X_INVALID_END_TAG,
loc
}
])
})
})

View File

@ -46,9 +46,12 @@ function createTransformContext(
}, },
...options, ...options,
parent: root, parent: root,
ancestors: [root], ancestors: [],
childIndex: 0, childIndex: 0,
replaceNode(node) { replaceNode(node) {
if (__DEV__ && context.nodeRemoved) {
throw new Error(`node being replaced is already removed`)
}
context.parent.children[context.childIndex] = node context.parent.children[context.childIndex] = node
}, },
removeNode() { removeNode() {
@ -85,9 +88,9 @@ function traverseNode(
// apply transform plugins // apply transform plugins
const transforms = context.transforms const transforms = context.transforms
for (let i = 0; i < transforms.length; i++) { for (let i = 0; i < transforms.length; i++) {
const transform = transforms[i] const plugin = transforms[i]
context.nodeRemoved = false context.nodeRemoved = false
transform(node, context) plugin(node, context)
if (context.nodeRemoved) { if (context.nodeRemoved) {
return return
} else { } else {