test: combine with transform and codegen tests for `v-bind` (#45)

Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
Rizumu Ayaka 2023-12-10 01:33:29 +08:00 committed by GitHub
parent 12250a85b9
commit b421aa91a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 118 additions and 139 deletions

View File

@ -1,6 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler: codegen v-bind > .camel modifier 1`] = ` exports[`compiler v-bind > .camel modifier 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor'; "import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) { export function render(_ctx) {
@ -14,7 +14,49 @@ export function render(_ctx) {
}" }"
`; `;
exports[`compiler: codegen v-bind > dynamic arg 1`] = ` exports[`compiler v-bind > .camel modifier w/ dynamic arg 1`] = `
"import { camelize as _camelize } from 'vue';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_effect(() => {
_setAttr(n1, _camelize(_ctx.foo), undefined, _ctx.id)
})
return n0
}"
`;
exports[`compiler v-bind > .camel modifier w/ no expression 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_effect(() => {
_setAttr(n1, "fooBar", undefined, _ctx.fooBar)
})
return n0
}"
`;
exports[`compiler v-bind > basic 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_effect(() => {
_setAttr(n1, "id", undefined, _ctx.id)
})
return n0
}"
`;
exports[`compiler v-bind > dynamic arg 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor'; "import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) { export function render(_ctx) {
@ -28,7 +70,7 @@ export function render(_ctx) {
}" }"
`; `;
exports[`compiler: codegen v-bind > no expression (shorthand) 1`] = ` exports[`compiler v-bind > no expression (shorthand) 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor'; "import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) { export function render(_ctx) {
@ -42,7 +84,7 @@ export function render(_ctx) {
}" }"
`; `;
exports[`compiler: codegen v-bind > no expression 1`] = ` exports[`compiler v-bind > no expression 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor'; "import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) { export function render(_ctx) {
@ -56,7 +98,7 @@ export function render(_ctx) {
}" }"
`; `;
exports[`compiler: codegen v-bind > should error if no expression 1`] = ` exports[`compiler v-bind > should error if empty expression 1`] = `
"import { template as _template } from 'vue/vapor'; "import { template as _template } from 'vue/vapor';
export function render(_ctx) { export function render(_ctx) {
@ -65,17 +107,3 @@ export function render(_ctx) {
return n0 return n0
}" }"
`; `;
exports[`compiler: codegen v-bind > simple expression 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_effect(() => {
_setAttr(n1, "id", undefined, _ctx.id)
})
return n0
}"
`;

View File

@ -1,9 +1,4 @@
import { import { ErrorCodes, NodeTypes } from '@vue/compiler-dom'
type RootNode,
ErrorCodes,
NodeTypes,
BindingTypes,
} from '@vue/compiler-dom'
import { import {
type RootIRNode, type RootIRNode,
type CompilerOptions, type CompilerOptions,
@ -13,48 +8,45 @@ import {
transformElement, transformElement,
IRNodeTypes, IRNodeTypes,
compile as _compile, compile as _compile,
generate,
} from '../../src' } from '../../src'
function parseWithVBind( function compileWithVBind(
template: string, template: string,
options: CompilerOptions = {}, options: CompilerOptions = {},
): RootIRNode { ): {
const ast = parse(template) ir: RootIRNode
code: string
} {
const ast = parse(template, { prefixIdentifiers: true, ...options })
const ir = transform(ast, { const ir = transform(ast, {
nodeTransforms: [transformElement], nodeTransforms: [transformElement],
directiveTransforms: { directiveTransforms: {
bind: transformVBind, bind: transformVBind,
}, },
...options,
})
return ir
}
function compile(template: string | RootNode, options: CompilerOptions = {}) {
let { code } = _compile(template, {
...options,
mode: 'module',
prefixIdentifiers: true, prefixIdentifiers: true,
...options,
}) })
return code const { code } = generate(ir, { prefixIdentifiers: true, ...options })
return { ir, code }
} }
describe('compiler: transform v-bind', () => { describe('compiler v-bind', () => {
test('basic', () => { test('basic', () => {
const node = parseWithVBind(`<div v-bind:id="id"/>`) const { ir, code } = compileWithVBind(`<div v-bind:id="id"/>`)
expect(node.dynamic.children[0]).toMatchObject({ expect(ir.dynamic.children[0]).toMatchObject({
id: 1, id: 1,
referenced: true, referenced: true,
}) })
expect(node.template[0]).toMatchObject({ expect(ir.template[0]).toMatchObject({
type: IRNodeTypes.TEMPLATE_FACTORY, type: IRNodeTypes.TEMPLATE_FACTORY,
template: '<div></div>', template: '<div></div>',
}) })
expect(node.effect).lengthOf(1) expect(ir.effect).lengthOf(1)
expect(node.effect[0].expressions).lengthOf(1) expect(ir.effect[0].expressions).lengthOf(1)
expect(node.effect[0].operations).lengthOf(1) expect(ir.effect[0].operations).lengthOf(1)
expect(node.effect[0]).toMatchObject({ expect(ir.effect[0]).toMatchObject({
expressions: [ expressions: [
{ {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
@ -89,12 +81,15 @@ describe('compiler: transform v-bind', () => {
}, },
], ],
}) })
expect(code).matchSnapshot()
expect(code).contains('_setAttr(n1, "id", undefined, _ctx.id)')
}) })
test('no expression', () => { test('no expression', () => {
const node = parseWithVBind(`<div v-bind:id />`) const { ir, code } = compileWithVBind(`<div v-bind:id />`)
expect(node.effect[0].operations[0]).toMatchObject({ expect(ir.effect[0].operations[0]).toMatchObject({
type: IRNodeTypes.SET_PROP, type: IRNodeTypes.SET_PROP,
key: { key: {
content: `id`, content: `id`,
@ -113,27 +108,35 @@ describe('compiler: transform v-bind', () => {
}, },
}, },
}) })
expect(code).matchSnapshot()
expect(code).contains('_setAttr(n1, "id", undefined, _ctx.id)')
}) })
test('no expression (shorthand)', () => { test('no expression (shorthand)', () => {
const node = parseWithVBind(`<div :id />`) const { ir, code } = compileWithVBind(`<div :camel-case />`)
expect(node.effect[0].operations[0]).toMatchObject({ expect(ir.effect[0].operations[0]).toMatchObject({
type: IRNodeTypes.SET_PROP, type: IRNodeTypes.SET_PROP,
key: { key: {
content: `id`, content: `camel-case`,
isStatic: true, isStatic: true,
}, },
value: { value: {
content: `id`, content: `camelCase`,
isStatic: false, isStatic: false,
}, },
}) })
expect(code).matchSnapshot()
expect(code).contains(
'_setAttr(n1, "camel-case", undefined, _ctx.camelCase)',
)
}) })
test('dynamic arg', () => { test('dynamic arg', () => {
const node = parseWithVBind(`<div v-bind:[id]="id"/>`) const { ir, code } = compileWithVBind(`<div v-bind:[id]="id"/>`)
expect(node.effect[0].operations[0]).toMatchObject({ expect(ir.effect[0].operations[0]).toMatchObject({
type: IRNodeTypes.SET_PROP, type: IRNodeTypes.SET_PROP,
element: 1, element: 1,
key: { key: {
@ -147,11 +150,17 @@ describe('compiler: transform v-bind', () => {
isStatic: false, isStatic: false,
}, },
}) })
expect(code).matchSnapshot()
expect(code).contains('_setAttr(n1, _ctx.id, undefined, _ctx.id)')
}) })
test('should error if empty expression', () => { test('should error if empty expression', () => {
const onError = vi.fn() const onError = vi.fn()
const node = parseWithVBind(`<div v-bind:arg="" />`, { onError }) const { ir, code } = compileWithVBind(`<div v-bind:arg="" />`, {
onError,
})
expect(onError.mock.calls[0][0]).toMatchObject({ expect(onError.mock.calls[0][0]).toMatchObject({
code: ErrorCodes.X_V_BIND_NO_EXPRESSION, code: ErrorCodes.X_V_BIND_NO_EXPRESSION,
loc: { loc: {
@ -159,15 +168,19 @@ describe('compiler: transform v-bind', () => {
end: { line: 1, column: 19 }, end: { line: 1, column: 19 },
}, },
}) })
expect(node.template[0]).toMatchObject({ expect(ir.template[0]).toMatchObject({
type: IRNodeTypes.TEMPLATE_FACTORY, type: IRNodeTypes.TEMPLATE_FACTORY,
template: '<div arg=""></div>', template: '<div arg=""></div>',
}) })
expect(code).matchSnapshot()
expect(code).contains(JSON.stringify('<div arg=""></div>'))
}) })
test('.camel modifier', () => { test('.camel modifier', () => {
const node = parseWithVBind(`<div v-bind:foo-bar.camel="id"/>`) const { ir, code } = compileWithVBind(`<div v-bind:foo-bar.camel="id"/>`)
expect(node.effect[0].operations[0]).toMatchObject({
expect(ir.effect[0].operations[0]).toMatchObject({
key: { key: {
content: `fooBar`, content: `fooBar`,
isStatic: true, isStatic: true,
@ -177,11 +190,15 @@ describe('compiler: transform v-bind', () => {
isStatic: false, isStatic: false,
}, },
}) })
expect(code).matchSnapshot()
expect(code).contains('_setAttr(n1, "fooBar", undefined, _ctx.id)')
}) })
test('.camel modifier w/ no expression', () => { test('.camel modifier w/ no expression', () => {
const node = parseWithVBind(`<div v-bind:foo-bar.camel />`) const { ir, code } = compileWithVBind(`<div v-bind:foo-bar.camel />`)
expect(node.effect[0].operations[0]).toMatchObject({
expect(ir.effect[0].operations[0]).toMatchObject({
key: { key: {
content: `fooBar`, content: `fooBar`,
isStatic: true, isStatic: true,
@ -191,11 +208,16 @@ describe('compiler: transform v-bind', () => {
isStatic: false, isStatic: false,
}, },
}) })
expect(code).matchSnapshot()
expect(code).contains('effect')
expect(code).contains('_setAttr(n1, "fooBar", undefined, _ctx.fooBar)')
}) })
test('.camel modifier w/ dynamic arg', () => { test('.camel modifier w/ dynamic arg', () => {
const node = parseWithVBind(`<div v-bind:[foo].camel="id"/>`) const { ir, code } = compileWithVBind(`<div v-bind:[foo].camel="id"/>`)
expect(node.effect[0].operations[0]).toMatchObject({
expect(ir.effect[0].operations[0]).toMatchObject({
runtimeCamelize: true, runtimeCamelize: true,
key: { key: {
content: `foo`, content: `foo`,
@ -206,6 +228,12 @@ describe('compiler: transform v-bind', () => {
isStatic: false, isStatic: false,
}, },
}) })
expect(code).matchSnapshot()
expect(code).contains('effect')
expect(code).contains(
`_setAttr(n1, _camelize(_ctx.foo), undefined, _ctx.id)`,
)
}) })
test.todo('.camel modifier w/ dynamic arg + prefixIdentifiers') test.todo('.camel modifier w/ dynamic arg + prefixIdentifiers')
@ -219,80 +247,3 @@ describe('compiler: transform v-bind', () => {
test.todo('.attr modifier') test.todo('.attr modifier')
test.todo('.attr modifier w/ no expression') test.todo('.attr modifier w/ no expression')
}) })
// TODO: combine with above
describe('compiler: codegen v-bind', () => {
test('simple expression', () => {
const code = compile(`<div :id="id"></div>`, {
bindingMetadata: {
id: BindingTypes.SETUP_REF,
},
})
expect(code).matchSnapshot()
})
test('should error if no expression', () => {
const onError = vi.fn()
const code = compile(`<div v-bind:arg="" />`, { onError })
expect(onError.mock.calls[0][0]).toMatchObject({
code: ErrorCodes.X_V_BIND_NO_EXPRESSION,
loc: {
start: {
line: 1,
column: 6,
},
end: {
line: 1,
column: 19,
},
},
})
expect(code).matchSnapshot()
// the arg is static
expect(code).contains(JSON.stringify('<div arg=""></div>'))
})
test('no expression', () => {
const code = compile('<div v-bind:id />', {
bindingMetadata: {
id: BindingTypes.SETUP_REF,
},
})
expect(code).matchSnapshot()
expect(code).contains('_setAttr(n1, "id", undefined, _ctx.id)')
})
test('no expression (shorthand)', () => {
const code = compile('<div :camel-case />', {
bindingMetadata: {
camelCase: BindingTypes.SETUP_REF,
},
})
expect(code).matchSnapshot()
expect(code).contains(
'_setAttr(n1, "camel-case", undefined, _ctx.camelCase)',
)
})
test('dynamic arg', () => {
const code = compile('<div v-bind:[id]="id"/>', {
bindingMetadata: {
id: BindingTypes.SETUP_REF,
},
})
expect(code).matchSnapshot()
expect(code).contains('_setAttr(n1, _ctx.id, undefined, _ctx.id)')
})
test('.camel modifier', () => {
const code = compile(`<div v-bind:foo-bar.camel="id"/>`)
expect(code).matchSnapshot()
expect(code).contains('fooBar')
})
})