test: improve coverage

This commit is contained in:
Evan You 2019-09-24 16:35:01 -04:00
parent e67084e5a1
commit cae03f616d
8 changed files with 318 additions and 52 deletions

View File

@ -5,8 +5,9 @@ exports[`compiler: codegen callExpression + objectExpression + arrayExpression 1
with (this) { with (this) {
return createVNode(\\"div\\", { return createVNode(\\"div\\", {
id: \\"foo\\", id: \\"foo\\",
[prop]: bar [prop]: bar,
}, [ [foo + bar]: bar
}, [createVNode(\\"p\\", { \\"some-key\\": \\"foo\\" })], [
foo, foo,
createVNode(\\"p\\") createVNode(\\"p\\")
]) ])
@ -22,6 +23,14 @@ exports[`compiler: codegen comment 1`] = `
}" }"
`; `;
exports[`compiler: codegen compound expression 1`] = `
"return function render() {
with (this) {
return toString(_ctx.foo)
}
}"
`;
exports[`compiler: codegen forNode 1`] = ` exports[`compiler: codegen forNode 1`] = `
"return function render() { "return function render() {
with (this) { with (this) {
@ -30,6 +39,30 @@ exports[`compiler: codegen forNode 1`] = `
}" }"
`; `;
exports[`compiler: codegen forNode w/ skipped key alias 1`] = `
"return function render() {
with (this) {
return renderList(list, (v, __key, i) => toString(v))
}
}"
`;
exports[`compiler: codegen forNode w/ skipped value alias 1`] = `
"return function render() {
with (this) {
return renderList(list, (__value, k, i) => toString(v))
}
}"
`;
exports[`compiler: codegen forNode w/ skipped value and key aliases 1`] = `
"return function render() {
with (this) {
return renderList(list, (__value, __key, i) => toString(v))
}
}"
`;
exports[`compiler: codegen function mode preamble 1`] = ` exports[`compiler: codegen function mode preamble 1`] = `
"const { helperOne, helperTwo } = Vue "const { helperOne, helperTwo } = Vue
@ -52,6 +85,18 @@ exports[`compiler: codegen ifNode 1`] = `
}" }"
`; `;
exports[`compiler: codegen ifNode with no v-else 1`] = `
"return function render() {
with (this) {
return (foo)
? \\"foo\\"
: (bar)
? toString(bye)
: null
}
}"
`;
exports[`compiler: codegen interpolation 1`] = ` exports[`compiler: codegen interpolation 1`] = `
"return function render() { "return function render() {
with (this) { with (this) {

View File

@ -7,9 +7,11 @@ import {
createExpression, createExpression,
Namespaces, Namespaces,
ElementTypes, ElementTypes,
CallExpression,
createObjectExpression, createObjectExpression,
createObjectProperty, createObjectProperty,
createArrayExpression createArrayExpression,
ElementNode
} from '../src' } from '../src'
import { SourceMapConsumer, RawSourceMap } from 'source-map' import { SourceMapConsumer, RawSourceMap } from 'source-map'
import { CREATE_VNODE, COMMENT, TO_STRING } from '../src/runtimeConstants' import { CREATE_VNODE, COMMENT, TO_STRING } from '../src/runtimeConstants'
@ -145,6 +147,25 @@ describe('compiler: codegen', () => {
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
}) })
test('compound expression', () => {
const { code } = generate(
createRoot({
children: [
{
type: NodeTypes.EXPRESSION,
content: 'foo',
isStatic: false,
isInterpolation: true,
loc: mockLoc,
children: [`_ctx.`, createExpression(`foo`, false, mockLoc)]
}
]
})
)
expect(code).toMatch(`return toString(_ctx.foo)`)
expect(code).toMatchSnapshot()
})
test('ifNode', () => { test('ifNode', () => {
const { code } = generate( const { code } = generate(
createRoot({ createRoot({
@ -202,6 +223,50 @@ describe('compiler: codegen', () => {
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
}) })
test('ifNode with no v-else', () => {
const { code } = generate(
createRoot({
children: [
{
type: NodeTypes.IF,
loc: mockLoc,
isRoot: true,
branches: [
{
type: NodeTypes.IF_BRANCH,
condition: createExpression('foo', false, mockLoc),
loc: mockLoc,
isRoot: true,
children: [
{
type: NodeTypes.TEXT,
content: 'foo',
isEmpty: false,
loc: mockLoc
}
]
},
{
type: NodeTypes.IF_BRANCH,
condition: createExpression('bar', false, mockLoc),
loc: mockLoc,
isRoot: true,
children: [createExpression(`bye`, false, mockLoc, true)]
}
]
}
]
})
)
expect(code).toMatch(`
return (foo)
? "foo"
: (bar)
? ${TO_STRING}(bye)
: null`)
expect(code).toMatchSnapshot()
})
test('forNode', () => { test('forNode', () => {
const { code } = generate( const { code } = generate(
createRoot({ createRoot({
@ -222,63 +287,166 @@ describe('compiler: codegen', () => {
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
}) })
test('callExpression + objectExpression + arrayExpression', () => { test('forNode w/ skipped value alias', () => {
const { code } = generate( const { code } = generate(
createRoot({ createRoot({
children: [ children: [
{ {
type: NodeTypes.ELEMENT, type: NodeTypes.FOR,
loc: mockLoc, loc: mockLoc,
ns: Namespaces.HTML, source: createExpression(`list`, false, mockLoc),
tag: 'div', valueAlias: undefined,
tagType: ElementTypes.ELEMENT, keyAlias: createExpression(`k`, false, mockLoc),
isSelfClosing: false, objectIndexAlias: createExpression(`i`, false, mockLoc),
props: [], children: [createExpression(`v`, false, mockLoc, true)]
children: [], }
codegenNode: { ]
type: NodeTypes.JS_CALL_EXPRESSION, })
loc: mockLoc, )
callee: CREATE_VNODE, expect(code).toMatch(`renderList(list, (__value, k, i) => toString(v))`)
arguments: [ expect(code).toMatchSnapshot()
`"div"`, })
test('forNode w/ skipped key alias', () => {
const { code } = generate(
createRoot({
children: [
{
type: NodeTypes.FOR,
loc: mockLoc,
source: createExpression(`list`, false, mockLoc),
valueAlias: createExpression(`v`, false, mockLoc),
keyAlias: undefined,
objectIndexAlias: createExpression(`i`, false, mockLoc),
children: [createExpression(`v`, false, mockLoc, true)]
}
]
})
)
expect(code).toMatch(`renderList(list, (v, __key, i) => toString(v))`)
expect(code).toMatchSnapshot()
})
test('forNode w/ skipped value and key aliases', () => {
const { code } = generate(
createRoot({
children: [
{
type: NodeTypes.FOR,
loc: mockLoc,
source: createExpression(`list`, false, mockLoc),
valueAlias: undefined,
keyAlias: undefined,
objectIndexAlias: createExpression(`i`, false, mockLoc),
children: [createExpression(`v`, false, mockLoc, true)]
}
]
})
)
expect(code).toMatch(`renderList(list, (__value, __key, i) => toString(v))`)
expect(code).toMatchSnapshot()
})
test('callExpression + objectExpression + arrayExpression', () => {
function createElementWithCodegen(
args: CallExpression['arguments']
): ElementNode {
return {
type: NodeTypes.ELEMENT,
loc: mockLoc,
ns: Namespaces.HTML,
tag: 'div',
tagType: ElementTypes.ELEMENT,
isSelfClosing: false,
props: [],
children: [],
codegenNode: {
type: NodeTypes.JS_CALL_EXPRESSION,
loc: mockLoc,
callee: CREATE_VNODE,
arguments: args
}
}
}
const { code } = generate(
createRoot({
children: [
createElementWithCodegen([
// string
`"div"`,
// ObjectExpression
createObjectExpression(
[
createObjectProperty(
createExpression(`id`, true, mockLoc),
createExpression(`foo`, true, mockLoc),
mockLoc
),
createObjectProperty(
createExpression(`prop`, false, mockLoc),
createExpression(`bar`, false, mockLoc),
mockLoc
),
// compound expression as computed key
createObjectProperty(
{
type: NodeTypes.EXPRESSION,
content: ``,
loc: mockLoc,
isStatic: false,
isInterpolation: false,
children: [
`foo + `,
createExpression(`bar`, false, mockLoc)
]
},
createExpression(`bar`, false, mockLoc),
mockLoc
)
],
mockLoc
),
// ChildNode[]
[
createElementWithCodegen([
`"p"`,
createObjectExpression( createObjectExpression(
[ [
createObjectProperty( createObjectProperty(
createExpression(`id`, true, mockLoc), // should quote the key!
createExpression(`some-key`, true, mockLoc),
createExpression(`foo`, true, mockLoc), createExpression(`foo`, true, mockLoc),
mockLoc mockLoc
),
createObjectProperty(
createExpression(`prop`, false, mockLoc),
createExpression(`bar`, false, mockLoc),
mockLoc
) )
], ],
mockLoc mockLoc
),
createArrayExpression(
[
'foo',
{
type: NodeTypes.JS_CALL_EXPRESSION,
loc: mockLoc,
callee: CREATE_VNODE,
arguments: [`"p"`]
}
],
mockLoc
) )
] ])
} ],
} // ArrayExpression
createArrayExpression(
[
'foo',
{
type: NodeTypes.JS_CALL_EXPRESSION,
loc: mockLoc,
callee: CREATE_VNODE,
arguments: [`"p"`]
}
],
mockLoc
)
])
] ]
}) })
) )
expect(code).toMatch(` expect(code).toMatch(`
return ${CREATE_VNODE}("div", { return ${CREATE_VNODE}("div", {
id: "foo", id: "foo",
[prop]: bar [prop]: bar,
}, [ [foo + bar]: bar
}, [${CREATE_VNODE}("p", { "some-key": "foo" })], [
foo, foo,
${CREATE_VNODE}("p") ${CREATE_VNODE}("p")
])`) ])`)

View File

@ -80,6 +80,46 @@ describe('compiler: element transform', () => {
]) ])
}) })
test('props + children', () => {
const { node } = parseWithElementTransform(`<div id="foo"><span/></div>`)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments).toMatchObject([
`"div"`,
createStaticObjectMatcher({
id: 'foo'
}),
[
{
type: NodeTypes.ELEMENT,
tag: 'span',
codegenNode: {
callee: CREATE_VNODE,
arguments: [`"span"`]
}
}
]
])
})
test('0 placeholder for children with no props', () => {
const { node } = parseWithElementTransform(`<div><span/></div>`)
expect(node.callee).toBe(CREATE_VNODE)
expect(node.arguments).toMatchObject([
`"div"`,
`0`,
[
{
type: NodeTypes.ELEMENT,
tag: 'span',
codegenNode: {
callee: CREATE_VNODE,
arguments: [`"span"`]
}
}
]
])
})
test('v-bind="obj"', () => { test('v-bind="obj"', () => {
const { root, node } = parseWithElementTransform(`<div v-bind="obj" />`) const { root, node } = parseWithElementTransform(`<div v-bind="obj" />`)
// single v-bind doesn't need mergeProps // single v-bind doesn't need mergeProps

View File

@ -5,16 +5,21 @@ import {
ElementNode, ElementNode,
DirectiveNode, DirectiveNode,
NodeTypes, NodeTypes,
ForNode ForNode,
CompilerOptions
} from '../../src' } from '../../src'
import { transformFor } from '../..//src/transforms/vFor' import { transformFor } from '../..//src/transforms/vFor'
import { transformExpression } from '../../src/transforms/transformExpression' import { transformExpression } from '../../src/transforms/transformExpression'
function parseWithExpressionTransform(template: string) { function parseWithExpressionTransform(
template: string,
options: CompilerOptions = {}
) {
const ast = parse(template) const ast = parse(template)
transform(ast, { transform(ast, {
prefixIdentifiers: true, prefixIdentifiers: true,
nodeTransforms: [transformFor, transformExpression] nodeTransforms: [transformFor, transformExpression],
...options
}) })
return ast.children[0] return ast.children[0]
} }
@ -297,4 +302,10 @@ describe('compiler: expression transform', () => {
`]` `]`
]) ])
}) })
test('should handle parse error', () => {
const onError = jest.fn()
parseWithExpressionTransform(`{{ a( }}`, { onError })
expect(onError.mock.calls[0][0].message).toMatch(`Expected ')'`)
})
}) })

View File

@ -277,6 +277,7 @@ function genNode(node: CodegenNode, context: CodegenContext) {
genArrayExpression(node, context) genArrayExpression(node, context)
break break
default: default:
/* istanbul ignore next */
__DEV__ && __DEV__ &&
assert(false, `unhandled codegen node type: ${(node as any).type}`) assert(false, `unhandled codegen node type: ${(node as any).type}`)
} }
@ -399,7 +400,7 @@ function genFor(node: ForNode, context: CodegenContext) {
} }
if (keyAlias) { if (keyAlias) {
if (!valueAlias) { if (!valueAlias) {
push(`_`) push(`__value`)
} }
push(`, `) push(`, `)
genExpression(keyAlias, context) genExpression(keyAlias, context)
@ -407,9 +408,9 @@ function genFor(node: ForNode, context: CodegenContext) {
if (objectIndexAlias) { if (objectIndexAlias) {
if (!keyAlias) { if (!keyAlias) {
if (!valueAlias) { if (!valueAlias) {
push(`_, __`) push(`__value, __key`)
} else { } else {
push(`__`) push(`, __key`)
} }
} }
push(`, `) push(`, `)
@ -447,12 +448,9 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
// value // value
genExpression(value, context) genExpression(value, context)
if (i < properties.length - 1) { if (i < properties.length - 1) {
if (multilines) { // will only reach this if it's multilines
push(`,`) push(`,`)
newline() newline()
} else {
push(`, `)
}
} }
} }
multilines && deindent() multilines && deindent()

View File

@ -199,6 +199,7 @@ function pushNode(
node: ChildNode node: ChildNode
): void { ): void {
// ignore comments in production // ignore comments in production
/* istanbul ignore next */
if (!__DEV__ && node.type === NodeTypes.COMMENT) { if (!__DEV__ && node.type === NodeTypes.COMMENT) {
return return
} }

View File

@ -85,6 +85,7 @@ function createTransformContext(
childIndex: 0, childIndex: 0,
currentNode: null, currentNode: null,
replaceNode(node) { replaceNode(node) {
/* istanbul ignore if */
if (__DEV__ && !context.currentNode) { if (__DEV__ && !context.currentNode) {
throw new Error(`node being replaced is already removed.`) throw new Error(`node being replaced is already removed.`)
} }
@ -97,6 +98,7 @@ function createTransformContext(
: context.currentNode : context.currentNode
? context.childIndex ? context.childIndex
: -1 : -1
/* istanbul ignore if */
if (__DEV__ && removalIndex < 0) { if (__DEV__ && removalIndex < 0) {
throw new Error(`node being removed is not a child of current parent`) throw new Error(`node being removed is not a child of current parent`)
} }

View File

@ -60,6 +60,7 @@ export function advancePositionWithMutation(
} }
export function assert(condition: boolean, msg?: string) { export function assert(condition: boolean, msg?: string) {
/* istanbul ignore if */
if (!condition) { if (!condition) {
throw new Error(msg || `unexpected compiler condition`) throw new Error(msg || `unexpected compiler condition`)
} }