test: compiler integration tests

This commit is contained in:
Evan You
2019-09-25 22:29:37 -04:00
parent b5d21aeff7
commit ac7587fdb5
11 changed files with 382 additions and 251 deletions

View File

@@ -154,12 +154,11 @@ return function render() {
`;
exports[`compiler: codegen module mode preamble 1`] = `
"import { helperOne, helperTwo } from 'vue'
"import { helperOne, helperTwo } from \\"vue\\"
export default function render() {
with (this) {
return null
}
const _ctx = this
return null
}"
`;

View File

@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`function mode 1`] = `
exports[`compiler: integration tests function mode 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
@@ -13,10 +13,50 @@ return function render() {
ok
? _createVNode(\\"div\\", 0, \\"yes\\")
: \\"no\\",
_renderList(list, (i, j) => {
return _createVNode(\\"div\\", 0, [_createVNode(\\"span\\", 0, _toString(i + j))])
_renderList(list, (value, index) => {
return _createVNode(\\"div\\", 0, [_createVNode(\\"span\\", 0, _toString(value + index))])
})
])
}
}"
`;
exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 1`] = `
"const { createVNode, toString, renderList } = Vue
return function render() {
const _ctx = this
return createVNode(\\"div\\", {
id: \\"foo\\",
class: _ctx.bar
}, [
toString(_ctx.world),
(_ctx.ok)
? createVNode(\\"div\\", 0, \\"yes\\")
: \\"no\\",
renderList(_ctx.list, (value, index) => {
return createVNode(\\"div\\", 0, [createVNode(\\"span\\", 0, toString(value + index))])
})
])
}"
`;
exports[`compiler: integration tests module mode 1`] = `
"import { createVNode, toString, renderList } from \\"vue\\"
export default function render() {
const _ctx = this
return createVNode(\\"div\\", {
id: \\"foo\\",
class: _ctx.bar
}, [
_toString(_ctx.world),
(_ctx.ok)
? createVNode(\\"div\\", 0, \\"yes\\")
: \\"no\\",
_renderList(_ctx.list, (value, index) => {
return createVNode(\\"div\\", 0, [createVNode(\\"span\\", 0, _toString(value + index))])
})
])
}"
`;

View File

@@ -51,7 +51,7 @@ describe('compiler: codegen', () => {
imports: [`helperOne`, `helperTwo`]
})
const { code } = generate(root, { mode: 'module' })
expect(code).toMatch(`import { helperOne, helperTwo } from 'vue'`)
expect(code).toMatch(`import { helperOne, helperTwo } from "vue"`)
expect(code).toMatchSnapshot()
})

View File

@@ -1,139 +1,227 @@
import { compile } from '../src'
import { SourceMapConsumer, RawSourceMap } from 'source-map'
// Integration tests for parser + transform + codegen
test('function mode', async () => {
describe('compiler: integration tests', () => {
const source = `
<div id="foo" :class="bar">
{{ world }}
<div v-if="ok">yes</div>
<template v-else>no</template>
<div v-for="(i, j) in list"><span>{{ i + j }}</span></div>
<div v-for="(value, index) in list"><span>{{ value + index }}</span></div>
</div>
`.trim()
const { code, map } = compile(source, {
sourceMap: true,
filename: `foo.vue`
function getPositionInCode(code: string, token: string) {
const generatedOffset = code.indexOf(token)
let line = 1
let lastNewLinePos = -1
for (let i = 0; i < generatedOffset; i++) {
if (code.charCodeAt(i) === 10 /* newline char code */) {
line++
lastNewLinePos = i
}
}
return {
line,
column:
lastNewLinePos === -1
? generatedOffset
: generatedOffset - lastNewLinePos - 1
}
}
test('function mode', async () => {
const { code, map } = compile(source, {
sourceMap: true,
filename: `foo.vue`
})
expect(code).toMatch(
`const { createVNode: _createVNode, toString: _toString, renderList: _renderList } = _Vue`
)
expect(code).toMatchSnapshot()
expect(map!.sources).toEqual([`foo.vue`])
expect(map!.sourcesContent).toEqual([source])
const consumer = await new SourceMapConsumer(map as RawSourceMap)
expect(
consumer.originalPositionFor(getPositionInCode(code, `id`))
).toMatchObject(getPositionInCode(source, `id`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `"foo"`))
).toMatchObject(getPositionInCode(source, `"foo"`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `class:`))
).toMatchObject(getPositionInCode(source, `class=`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `bar`))
).toMatchObject(getPositionInCode(source, `bar`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `world`))
).toMatchObject(getPositionInCode(source, `{{ world }}`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `ok`))
).toMatchObject(getPositionInCode(source, `ok`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `list`))
).toMatchObject(getPositionInCode(source, `list`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `value`))
).toMatchObject(getPositionInCode(source, `value`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `index`))
).toMatchObject(getPositionInCode(source, `index`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `value + index`))
).toMatchObject(getPositionInCode(source, `{{ value + index }}`))
})
expect(code).toMatchSnapshot()
expect(map!.sources).toEqual([`foo.vue`])
expect(map!.sourcesContent).toEqual([source])
const consumer = await new SourceMapConsumer(map as RawSourceMap)
// id=
expect(
consumer.originalPositionFor({
line: 6,
column: 6
test('function mode w/ prefixIdentifiers: true', async () => {
const { code, map } = compile(source, {
sourceMap: true,
filename: `foo.vue`,
prefixIdentifiers: true
})
).toMatchObject({
line: 1,
column: 5
expect(code).toMatch(`const { createVNode, toString, renderList } = Vue`)
expect(code).toMatchSnapshot()
expect(map!.sources).toEqual([`foo.vue`])
expect(map!.sourcesContent).toEqual([source])
const consumer = await new SourceMapConsumer(map as RawSourceMap)
expect(
consumer.originalPositionFor(getPositionInCode(code, `id`))
).toMatchObject(getPositionInCode(source, `id`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `"foo"`))
).toMatchObject(getPositionInCode(source, `"foo"`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `class:`))
).toMatchObject(getPositionInCode(source, `class=`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `bar`))
).toMatchObject(getPositionInCode(source, `bar`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `_ctx.bar`))
).toMatchObject(getPositionInCode(source, `bar`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `world`))
).toMatchObject(getPositionInCode(source, `{{ world }}`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `_ctx.world`))
).toMatchObject(getPositionInCode(source, `{{ world }}`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `ok`))
).toMatchObject(getPositionInCode(source, `ok`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `_ctx.ok`))
).toMatchObject(getPositionInCode(source, `ok`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `list`))
).toMatchObject(getPositionInCode(source, `list`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `_ctx.list`))
).toMatchObject(getPositionInCode(source, `list`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `value`))
).toMatchObject(getPositionInCode(source, `value`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `index`))
).toMatchObject(getPositionInCode(source, `index`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `value + index`))
).toMatchObject(getPositionInCode(source, `value + index`))
})
// "foo"
expect(
consumer.originalPositionFor({
line: 6,
column: 10
test('module mode', async () => {
const { code, map } = compile(source, {
mode: 'module',
sourceMap: true,
filename: `foo.vue`
})
).toMatchObject({
line: 1,
column: 8
})
// :class=
expect(
consumer.originalPositionFor({
line: 7,
column: 6
})
).toMatchObject({
line: 1,
column: 15
})
// bar
expect(
consumer.originalPositionFor({
line: 7,
column: 13
})
).toMatchObject({
line: 1,
column: 22
})
expect(code).toMatch(
`import { createVNode, toString, renderList } from "vue"`
)
// {{ world }}
expect(
consumer.originalPositionFor({
line: 9,
column: 16
})
).toMatchObject({
line: 2,
column: 2
})
expect(code).toMatchSnapshot()
expect(map!.sources).toEqual([`foo.vue`])
expect(map!.sourcesContent).toEqual([source])
// ok
expect(
consumer.originalPositionFor({
line: 10,
column: 6
})
).toMatchObject({
line: 3,
column: 13
})
const consumer = await new SourceMapConsumer(map as RawSourceMap)
// i
expect(
consumer.originalPositionFor({
line: 13,
column: 25
})
).toMatchObject({
line: 5,
column: 15
})
expect(
consumer.originalPositionFor(getPositionInCode(code, `id`))
).toMatchObject(getPositionInCode(source, `id`))
// j
expect(
consumer.originalPositionFor({
line: 13,
column: 28
})
).toMatchObject({
line: 5,
column: 18
})
expect(
consumer.originalPositionFor(getPositionInCode(code, `"foo"`))
).toMatchObject(getPositionInCode(source, `"foo"`))
// list
expect(
consumer.originalPositionFor({
line: 13,
column: 18
})
).toMatchObject({
line: 5,
column: 24
})
expect(
consumer.originalPositionFor(getPositionInCode(code, `class:`))
).toMatchObject(getPositionInCode(source, `class=`))
// i + j
expect(
consumer.originalPositionFor({
line: 14,
column: 81
})
).toMatchObject({
line: 5,
column: 36
expect(
consumer.originalPositionFor(getPositionInCode(code, `bar`))
).toMatchObject(getPositionInCode(source, `bar`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `_ctx.bar`))
).toMatchObject(getPositionInCode(source, `bar`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `world`))
).toMatchObject(getPositionInCode(source, `{{ world }}`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `_ctx.world`))
).toMatchObject(getPositionInCode(source, `{{ world }}`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `ok`))
).toMatchObject(getPositionInCode(source, `ok`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `_ctx.ok`))
).toMatchObject(getPositionInCode(source, `ok`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `list`))
).toMatchObject(getPositionInCode(source, `list`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `_ctx.list`))
).toMatchObject(getPositionInCode(source, `list`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `value`))
).toMatchObject(getPositionInCode(source, `value`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `index`))
).toMatchObject(getPositionInCode(source, `index`))
expect(
consumer.originalPositionFor(getPositionInCode(code, `value + index`))
).toMatchObject(getPositionInCode(source, `value + index`))
})
})
test.todo('function mode w/ prefixIdentifiers: true')
test.todo('module mode')
test.todo('module mode w/ prefixIdentifiers: true')

View File

@@ -29,26 +29,17 @@ function parseWithExpressionTransform(
describe('compiler: expression transform', () => {
test('interpolation (root)', () => {
const node = parseWithExpressionTransform(`{{ foo }}`) as ExpressionNode
expect(node.children).toMatchObject([
`_ctx.`,
{
content: `foo`,
loc: node.loc
}
])
expect(node.children).toBeUndefined()
expect(node.content).toBe(`_ctx.foo`)
})
test('interpolation (children)', () => {
const node = parseWithExpressionTransform(
const el = parseWithExpressionTransform(
`<div>{{ foo }}</div>`
) as ElementNode
expect((node.children[0] as ExpressionNode).children).toMatchObject([
`_ctx.`,
{
content: `foo`,
loc: node.children[0].loc
}
])
const node = el.children[0] as ExpressionNode
expect(node.children).toBeUndefined()
expect(node.content).toBe(`_ctx.foo`)
})
test('directive value', () => {
@@ -57,13 +48,8 @@ describe('compiler: expression transform', () => {
) as ElementNode
expect((node.props[0] as DirectiveNode).arg!.children).toBeUndefined()
const exp = (node.props[0] as DirectiveNode).exp!
expect(exp.children).toMatchObject([
`_ctx.`,
{
content: `baz`,
loc: exp.loc
}
])
expect(exp.children).toBeUndefined()
expect(exp.content).toBe(`_ctx.baz`)
})
test('dynamic directive arg', () => {
@@ -72,20 +58,10 @@ describe('compiler: expression transform', () => {
) as ElementNode
const arg = (node.props[0] as DirectiveNode).arg!
const exp = (node.props[0] as DirectiveNode).exp!
expect(arg.children).toMatchObject([
`_ctx.`,
{
content: `arg`,
loc: arg.loc
}
])
expect(exp.children).toMatchObject([
`_ctx.`,
{
content: `baz`,
loc: exp.loc
}
])
expect(arg.children).toBeUndefined()
expect(arg.content).toBe(`_ctx.arg`)
expect(exp.children).toBeUndefined()
expect(exp.content).toBe(`_ctx.baz`)
})
test('should prefix complex expressions', () => {
@@ -94,9 +70,8 @@ describe('compiler: expression transform', () => {
) as ExpressionNode
// should parse into compound expression
expect(node.children).toMatchObject([
`_ctx.`,
{
content: `foo`,
content: `_ctx.foo`,
loc: {
source: `foo`,
start: {
@@ -111,9 +86,9 @@ describe('compiler: expression transform', () => {
}
}
},
`(_ctx.`,
`(`,
{
content: `baz`,
content: `_ctx.baz`,
loc: {
source: `baz`,
start: {
@@ -128,9 +103,9 @@ describe('compiler: expression transform', () => {
}
}
},
` + 1, { key: _ctx.`,
` + 1, { key: `,
{
content: `kuz`,
content: `_ctx.kuz`,
loc: {
source: `kuz`,
start: {
@@ -151,17 +126,16 @@ describe('compiler: expression transform', () => {
test('should prefix v-if condition', () => {
const node = parseWithExpressionTransform(`<div v-if="ok"/>`) as IfNode
expect(node.branches[0].condition!.children).toMatchObject([
`_ctx.`,
{ content: `ok` }
])
expect(node.branches[0].condition!.children).toBeUndefined()
expect(node.branches[0].condition!.content).toBe(`_ctx.ok`)
})
test('should prefix v-for source', () => {
const node = parseWithExpressionTransform(
`<div v-for="i in list"/>`
) as ForNode
expect(node.source.children).toMatchObject([`_ctx.`, { content: `list` }])
expect(node.source.children).toBeUndefined()
expect(node.source.content).toBe(`_ctx.list`)
})
test('should not prefix v-for alias', () => {
@@ -177,7 +151,8 @@ describe('compiler: expression transform', () => {
const j = div.children[1] as ExpressionNode
expect(j.type).toBe(NodeTypes.EXPRESSION)
expect(j.children).toMatchObject([`_ctx.`, { content: `j` }])
expect(j.children).toBeUndefined()
expect(j.content).toBe(`_ctx.j`)
})
test('should not prefix v-for aliases (multiple)', () => {
@@ -188,12 +163,19 @@ describe('compiler: expression transform', () => {
const exp = div.children[0] as ExpressionNode
expect(exp.type).toBe(NodeTypes.EXPRESSION)
expect(exp.content).toBe(`i + j + k`)
expect(exp.children).toBeUndefined()
// parsed for better source-map support
expect(exp.children).toMatchObject([
{ content: `i` },
` + `,
{ content: `j` },
` + `,
{ content: `k` }
])
const l = div.children[1] as ExpressionNode
expect(l.type).toBe(NodeTypes.EXPRESSION)
expect(l.children).toMatchObject([`_ctx.`, { content: `l` }])
expect(l.children).toBeUndefined()
expect(l.content).toBe(`_ctx.l`)
})
test('should prefix id outside of v-for', () => {
@@ -202,8 +184,8 @@ describe('compiler: expression transform', () => {
) as ElementNode
const exp = node.children[1] as ExpressionNode
expect(exp.type).toBe(NodeTypes.EXPRESSION)
expect(exp.content).toBe(`i`)
expect(exp.children).toMatchObject([`_ctx.`, { content: `i` }])
expect(exp.children).toBeUndefined()
expect(exp.content).toBe(`_ctx.i`)
})
test('nested v-for', () => {
@@ -217,7 +199,11 @@ describe('compiler: expression transform', () => {
const innerExp = (innerFor.children[0] as ElementNode)
.children[0] as ExpressionNode
expect(innerExp.type).toBe(NodeTypes.EXPRESSION)
expect(innerExp.children).toMatchObject([`i + _ctx.`, { content: `j` }])
expect(innerExp.children).toMatchObject([
{ content: 'i' },
` + `,
{ content: `_ctx.j` }
])
// when an inner v-for shadows a variable of an outer v-for and exit,
// it should not cause the outer v-for's alias to be removed from known ids
@@ -232,8 +218,12 @@ describe('compiler: expression transform', () => {
`{{ Math.max(1, 2) }}`
) as ExpressionNode
expect(node.type).toBe(NodeTypes.EXPRESSION)
expect(node.content).toBe(`Math.max(1, 2)`)
expect(node.children).toBeUndefined()
expect(node.children).toMatchObject([
{ content: `Math` },
`.`,
{ content: `max` },
`(1, 2)`
])
})
test('should not prefix id of a function declaration', () => {
@@ -242,8 +232,10 @@ describe('compiler: expression transform', () => {
) as ExpressionNode
expect(node.type).toBe(NodeTypes.EXPRESSION)
expect(node.children).toMatchObject([
`function foo() { return _ctx.`,
{ content: `bar` },
`function `,
{ content: `foo` },
`() { return `,
{ content: `_ctx.bar` },
` }`
])
})
@@ -254,8 +246,11 @@ describe('compiler: expression transform', () => {
) as ExpressionNode
expect(node.type).toBe(NodeTypes.EXPRESSION)
expect(node.children).toMatchObject([
`foo => foo + _ctx.`,
{ content: `bar` }
{ content: `foo` },
` => `,
{ content: `foo` },
` + `,
{ content: `_ctx.bar` }
])
})
@@ -265,8 +260,8 @@ describe('compiler: expression transform', () => {
) as ExpressionNode
expect(node.type).toBe(NodeTypes.EXPRESSION)
expect(node.children).toMatchObject([
`{ foo: _ctx.`,
{ content: `bar` },
`{ foo: `,
{ content: `_ctx.bar` },
` }`
])
})
@@ -277,10 +272,10 @@ describe('compiler: expression transform', () => {
) as ExpressionNode
expect(node.type).toBe(NodeTypes.EXPRESSION)
expect(node.children).toMatchObject([
`{ [_ctx.`,
{ content: `foo` },
`]: _ctx.`,
{ content: `bar` },
`{ [`,
{ content: `_ctx.foo` },
`]: `,
{ content: `_ctx.bar` },
` }`
])
})
@@ -288,8 +283,8 @@ describe('compiler: expression transform', () => {
test('should prefix object property shorthand value', () => {
const node = parseWithExpressionTransform(`{{ { foo } }}`) as ExpressionNode
expect(node.children).toMatchObject([
`{ foo: _ctx.`,
{ content: `foo` },
`{ foo: `,
{ content: `_ctx.foo` },
` }`
])
})
@@ -299,9 +294,11 @@ describe('compiler: expression transform', () => {
`{{ foo.bar.baz }}`
) as ExpressionNode
expect(node.children).toMatchObject([
`_ctx.`,
{ content: `foo` },
`.bar.baz`
{ content: `_ctx.foo` },
`.`,
{ content: `bar` },
`.`,
{ content: `baz` }
])
})
@@ -310,12 +307,11 @@ describe('compiler: expression transform', () => {
`{{ foo[bar][baz] }}`
) as ExpressionNode
expect(node.children).toMatchObject([
`_ctx.`,
{ content: `foo` },
`[_ctx.`,
{ content: `bar` },
`][_ctx.`,
{ content: 'baz' },
{ content: `_ctx.foo` },
`[`,
{ content: `_ctx.bar` },
`][`,
{ content: '_ctx.baz' },
`]`
])
})

View File

@@ -25,7 +25,7 @@ function parseWithVOn(
return ast.children[0] as ElementNode
}
describe('compiler: transform v-bind', () => {
describe('compiler: transform v-on', () => {
test('basic', () => {
const node = parseWithVOn(`<div v-on:click="onClick"/>`)
const props = node.codegenNode!.arguments[1] as ObjectExpression
@@ -76,8 +76,8 @@ describe('compiler: transform v-bind', () => {
const props = node.codegenNode!.arguments[1] as ObjectExpression
expect(props.properties[0]).toMatchObject({
key: {
content: `"on" + event`,
isStatic: false
isStatic: false,
children: [`"on" + `, { content: `event` }]
},
value: {
content: `handler`,
@@ -94,10 +94,10 @@ describe('compiler: transform v-bind', () => {
expect(props.properties[0]).toMatchObject({
key: {
isStatic: false,
children: [`"on" + `, `_ctx.`, { content: `event` }]
children: [`"on" + `, { content: `_ctx.event` }]
},
value: {
content: `handler`,
content: `_ctx.handler`,
isStatic: false
}
})