test(compiler): transformIf
This commit is contained in:
257
packages/compiler-core/__tests__/directives/vIf.spec.ts
Normal file
257
packages/compiler-core/__tests__/directives/vIf.spec.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
import { parse } from '../../src/parse'
|
||||
import { transform } from '../../src/transform'
|
||||
import { transformIf } from '../../src/directives/vIf'
|
||||
import {
|
||||
IfNode,
|
||||
NodeTypes,
|
||||
ElementNode,
|
||||
TextNode,
|
||||
CommentNode
|
||||
} from '../../src/ast'
|
||||
import { ErrorCodes } from '../../src/errors'
|
||||
|
||||
describe('compiler: v-if', () => {
|
||||
describe('transform', () => {
|
||||
test('basic v-if', () => {
|
||||
const ast = parse(`<div v-if="ok"/>`)
|
||||
transform(ast, {
|
||||
transforms: [transformIf]
|
||||
})
|
||||
const node = ast.children[0] as IfNode
|
||||
expect(node.type).toBe(NodeTypes.IF)
|
||||
expect(node.branches.length).toBe(1)
|
||||
expect(node.branches[0].condition!.content).toBe(`ok`)
|
||||
expect(node.branches[0].children.length).toBe(1)
|
||||
expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect((node.branches[0].children[0] as ElementNode).tag).toBe(`div`)
|
||||
})
|
||||
|
||||
test('template v-if', () => {
|
||||
const ast = parse(`<template v-if="ok"><div/>hello<p/></template>`)
|
||||
transform(ast, {
|
||||
transforms: [transformIf]
|
||||
})
|
||||
const node = ast.children[0] as IfNode
|
||||
expect(node.type).toBe(NodeTypes.IF)
|
||||
expect(node.branches.length).toBe(1)
|
||||
expect(node.branches[0].condition!.content).toBe(`ok`)
|
||||
expect(node.branches[0].children.length).toBe(3)
|
||||
expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect((node.branches[0].children[0] as ElementNode).tag).toBe(`div`)
|
||||
expect(node.branches[0].children[1].type).toBe(NodeTypes.TEXT)
|
||||
expect((node.branches[0].children[1] as TextNode).content).toBe(`hello`)
|
||||
expect(node.branches[0].children[2].type).toBe(NodeTypes.ELEMENT)
|
||||
expect((node.branches[0].children[2] as ElementNode).tag).toBe(`p`)
|
||||
})
|
||||
|
||||
test('v-if + v-else', () => {
|
||||
const ast = parse(`<div v-if="ok"/><p v-else/>`)
|
||||
transform(ast, {
|
||||
transforms: [transformIf]
|
||||
})
|
||||
// should fold branches
|
||||
expect(ast.children.length).toBe(1)
|
||||
|
||||
const node = ast.children[0] as IfNode
|
||||
expect(node.type).toBe(NodeTypes.IF)
|
||||
expect(node.branches.length).toBe(2)
|
||||
|
||||
const b1 = node.branches[0]
|
||||
expect(b1.condition!.content).toBe(`ok`)
|
||||
expect(b1.children.length).toBe(1)
|
||||
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
||||
|
||||
const b2 = node.branches[1]
|
||||
expect(b2.condition).toBeUndefined()
|
||||
expect(b2.children.length).toBe(1)
|
||||
expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect((b2.children[0] as ElementNode).tag).toBe(`p`)
|
||||
})
|
||||
|
||||
test('v-if + v-else-if', () => {
|
||||
const ast = parse(`<div v-if="ok"/><p v-else-if="orNot"/>`)
|
||||
transform(ast, {
|
||||
transforms: [transformIf]
|
||||
})
|
||||
// should fold branches
|
||||
expect(ast.children.length).toBe(1)
|
||||
|
||||
const node = ast.children[0] as IfNode
|
||||
expect(node.type).toBe(NodeTypes.IF)
|
||||
expect(node.branches.length).toBe(2)
|
||||
|
||||
const b1 = node.branches[0]
|
||||
expect(b1.condition!.content).toBe(`ok`)
|
||||
expect(b1.children.length).toBe(1)
|
||||
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
||||
|
||||
const b2 = node.branches[1]
|
||||
expect(b2.condition!.content).toBe(`orNot`)
|
||||
expect(b2.children.length).toBe(1)
|
||||
expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect((b2.children[0] as ElementNode).tag).toBe(`p`)
|
||||
})
|
||||
|
||||
test('v-if + v-else-if + v-else', () => {
|
||||
const ast = parse(
|
||||
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
|
||||
)
|
||||
transform(ast, {
|
||||
transforms: [transformIf]
|
||||
})
|
||||
// should fold branches
|
||||
expect(ast.children.length).toBe(1)
|
||||
|
||||
const node = ast.children[0] as IfNode
|
||||
expect(node.type).toBe(NodeTypes.IF)
|
||||
expect(node.branches.length).toBe(3)
|
||||
|
||||
const b1 = node.branches[0]
|
||||
expect(b1.condition!.content).toBe(`ok`)
|
||||
expect(b1.children.length).toBe(1)
|
||||
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
||||
|
||||
const b2 = node.branches[1]
|
||||
expect(b2.condition!.content).toBe(`orNot`)
|
||||
expect(b2.children.length).toBe(1)
|
||||
expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect((b2.children[0] as ElementNode).tag).toBe(`p`)
|
||||
|
||||
const b3 = node.branches[2]
|
||||
expect(b3.condition).toBeUndefined()
|
||||
expect(b3.children.length).toBe(1)
|
||||
expect(b3.children[0].type).toBe(NodeTypes.TEXT)
|
||||
expect((b3.children[0] as TextNode).content).toBe(`fine`)
|
||||
})
|
||||
|
||||
test('comment between branches', () => {
|
||||
const ast = parse(`
|
||||
<div v-if="ok"/>
|
||||
<!--foo-->
|
||||
<p v-else-if="orNot"/>
|
||||
<!--bar-->
|
||||
<template v-else>fine</template>
|
||||
`)
|
||||
transform(ast, {
|
||||
transforms: [transformIf]
|
||||
})
|
||||
// should fold branches
|
||||
expect(ast.children.length).toBe(1)
|
||||
|
||||
const node = ast.children[0] as IfNode
|
||||
expect(node.type).toBe(NodeTypes.IF)
|
||||
expect(node.branches.length).toBe(3)
|
||||
|
||||
const b1 = node.branches[0]
|
||||
expect(b1.condition!.content).toBe(`ok`)
|
||||
expect(b1.children.length).toBe(1)
|
||||
expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
|
||||
expect((b1.children[0] as ElementNode).tag).toBe(`div`)
|
||||
|
||||
const b2 = node.branches[1]
|
||||
expect(b2.condition!.content).toBe(`orNot`)
|
||||
expect(b2.children.length).toBe(2)
|
||||
expect(b2.children[0].type).toBe(NodeTypes.COMMENT)
|
||||
expect((b2.children[0] as CommentNode).content).toBe(`foo`)
|
||||
expect(b2.children[1].type).toBe(NodeTypes.ELEMENT)
|
||||
expect((b2.children[1] as ElementNode).tag).toBe(`p`)
|
||||
|
||||
const b3 = node.branches[2]
|
||||
expect(b3.condition).toBeUndefined()
|
||||
expect(b3.children.length).toBe(2)
|
||||
expect(b3.children[0].type).toBe(NodeTypes.COMMENT)
|
||||
expect((b3.children[0] as CommentNode).content).toBe(`bar`)
|
||||
expect(b3.children[1].type).toBe(NodeTypes.TEXT)
|
||||
expect((b3.children[1] as TextNode).content).toBe(`fine`)
|
||||
})
|
||||
|
||||
test('error on v-else missing adjacent v-if', () => {
|
||||
const ast = parse(`<div v-else/>`)
|
||||
const spy = jest.fn()
|
||||
transform(ast, {
|
||||
transforms: [transformIf],
|
||||
onError: spy
|
||||
})
|
||||
expect(spy.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
||||
loc: ast.children[0].loc.start
|
||||
}
|
||||
])
|
||||
|
||||
const ast2 = parse(`<div/><div v-else/>`)
|
||||
const spy2 = jest.fn()
|
||||
transform(ast2, {
|
||||
transforms: [transformIf],
|
||||
onError: spy2
|
||||
})
|
||||
expect(spy2.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
||||
loc: ast2.children[1].loc.start
|
||||
}
|
||||
])
|
||||
|
||||
const ast3 = parse(`<div/>foo<div v-else/>`)
|
||||
const spy3 = jest.fn()
|
||||
transform(ast3, {
|
||||
transforms: [transformIf],
|
||||
onError: spy3
|
||||
})
|
||||
expect(spy3.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
|
||||
loc: ast3.children[2].loc.start
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
test('error on v-else-if missing adjacent v-if', () => {
|
||||
const ast = parse(`<div v-else-if="foo"/>`)
|
||||
const spy = jest.fn()
|
||||
transform(ast, {
|
||||
transforms: [transformIf],
|
||||
onError: spy
|
||||
})
|
||||
expect(spy.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
||||
loc: ast.children[0].loc.start
|
||||
}
|
||||
])
|
||||
|
||||
const ast2 = parse(`<div/><div v-else-if="foo"/>`)
|
||||
const spy2 = jest.fn()
|
||||
transform(ast2, {
|
||||
transforms: [transformIf],
|
||||
onError: spy2
|
||||
})
|
||||
expect(spy2.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
||||
loc: ast2.children[1].loc.start
|
||||
}
|
||||
])
|
||||
|
||||
const ast3 = parse(`<div/>foo<div v-else-if="foo"/>`)
|
||||
const spy3 = jest.fn()
|
||||
transform(ast3, {
|
||||
transforms: [transformIf],
|
||||
onError: spy3
|
||||
})
|
||||
expect(spy3.mock.calls[0]).toMatchObject([
|
||||
{
|
||||
code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
|
||||
loc: ast3.children[2].loc.start
|
||||
}
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('codegen', () => {
|
||||
// TODO
|
||||
})
|
||||
})
|
||||
@@ -25,7 +25,7 @@ describe('compiler: transform', () => {
|
||||
{
|
||||
parent: ast,
|
||||
ancestors: [ast],
|
||||
childIndex: 0
|
||||
currentNode: div
|
||||
}
|
||||
])
|
||||
expect(calls[1]).toMatchObject([
|
||||
@@ -33,7 +33,7 @@ describe('compiler: transform', () => {
|
||||
{
|
||||
parent: div,
|
||||
ancestors: [ast, div],
|
||||
childIndex: 0
|
||||
currentNode: div.children[0]
|
||||
}
|
||||
])
|
||||
expect(calls[2]).toMatchObject([
|
||||
@@ -41,7 +41,7 @@ describe('compiler: transform', () => {
|
||||
{
|
||||
parent: div,
|
||||
ancestors: [ast, div],
|
||||
childIndex: 1
|
||||
currentNode: div.children[1]
|
||||
}
|
||||
])
|
||||
})
|
||||
@@ -81,7 +81,7 @@ describe('compiler: transform', () => {
|
||||
})
|
||||
|
||||
test('context.removeNode', () => {
|
||||
const ast = parse(`<span/><div/><span/>`)
|
||||
const ast = parse(`<span/><div>hello</div><span/>`)
|
||||
const c1 = ast.children[0]
|
||||
const c2 = ast.children[2]
|
||||
|
||||
@@ -99,12 +99,67 @@ describe('compiler: transform', () => {
|
||||
expect(ast.children[0]).toBe(c1)
|
||||
expect(ast.children[1]).toBe(c2)
|
||||
|
||||
// should not traverse children of remove node
|
||||
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('context.removeNode (prev sibling)', () => {
|
||||
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()
|
||||
// remove previous sibling
|
||||
context.removeNode(context.parent.children[0])
|
||||
}
|
||||
}
|
||||
const spy = jest.fn(plugin)
|
||||
transform(ast, {
|
||||
transforms: [spy]
|
||||
})
|
||||
|
||||
expect(ast.children.length).toBe(1)
|
||||
expect(ast.children[0]).toBe(c2)
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(3)
|
||||
// should still traverse first span before removal
|
||||
expect(spy.mock.calls[0][0]).toBe(c1)
|
||||
// should still traverse last span
|
||||
expect(spy.mock.calls[2][0]).toBe(c2)
|
||||
})
|
||||
|
||||
test('context.removeNode (next sibling)', () => {
|
||||
const ast = parse(`<span/><div/><span/>`)
|
||||
const c1 = ast.children[0]
|
||||
const d1 = ast.children[1]
|
||||
|
||||
const plugin: Transform = (node, context) => {
|
||||
if (node.type === NodeTypes.ELEMENT && node.tag === 'div') {
|
||||
context.removeNode()
|
||||
// remove next sibling
|
||||
context.removeNode(context.parent.children[1])
|
||||
}
|
||||
}
|
||||
const spy = jest.fn(plugin)
|
||||
transform(ast, {
|
||||
transforms: [spy]
|
||||
})
|
||||
|
||||
expect(ast.children.length).toBe(1)
|
||||
expect(ast.children[0]).toBe(c1)
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(2)
|
||||
// should still traverse first span before removal
|
||||
expect(spy.mock.calls[0][0]).toBe(c1)
|
||||
// should not traverse last span
|
||||
expect(spy.mock.calls[1][0]).toBe(d1)
|
||||
})
|
||||
|
||||
test('onError option', () => {
|
||||
const ast = parse(`<div/>`)
|
||||
const loc = ast.children[0].loc.start
|
||||
|
||||
Reference in New Issue
Block a user