|
|
@@ -0,0 +1,355 @@
|
|
|
+import {
|
|
|
+ parse,
|
|
|
+ transform,
|
|
|
+ generate,
|
|
|
+ ElementNode,
|
|
|
+ ObjectExpression,
|
|
|
+ CompilerOptions,
|
|
|
+ CallExpression
|
|
|
+} from '../../src'
|
|
|
+import { ErrorCodes } from '../../src/errors'
|
|
|
+import { transformModel } from '../../src/transforms/vModel'
|
|
|
+import { transformElement } from '../../src/transforms/transformElement'
|
|
|
+import { transformExpression } from '../../src/transforms/transformExpression'
|
|
|
+
|
|
|
+function parseWithVModel(template: string, options: CompilerOptions = {}) {
|
|
|
+ const ast = parse(template)
|
|
|
+
|
|
|
+ transform(ast, {
|
|
|
+ nodeTransforms: [transformExpression, transformElement],
|
|
|
+ directiveTransforms: {
|
|
|
+ ...options.directiveTransforms,
|
|
|
+ model: transformModel
|
|
|
+ },
|
|
|
+ ...options
|
|
|
+ })
|
|
|
+
|
|
|
+ return ast
|
|
|
+}
|
|
|
+
|
|
|
+describe('compiler: transform v-model', () => {
|
|
|
+ test('simple exprssion', () => {
|
|
|
+ const root = parseWithVModel('<input v-model="model" />')
|
|
|
+ const node = root.children[0] as ElementNode
|
|
|
+ const props = ((node.codegenNode as CallExpression)
|
|
|
+ .arguments[1] as ObjectExpression).properties
|
|
|
+
|
|
|
+ expect(props[0]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: 'modelValue',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ content: 'model',
|
|
|
+ isStatic: false
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(props[1]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: 'onUpdate:modelValue',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ children: [
|
|
|
+ '$event => (',
|
|
|
+ {
|
|
|
+ content: 'model',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ ' = $event)'
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(generate(root).code).toMatchSnapshot()
|
|
|
+ })
|
|
|
+
|
|
|
+ test('simple exprssion (with prefixIdentifiers)', () => {
|
|
|
+ const root = parseWithVModel('<input v-model="model" />', {
|
|
|
+ prefixIdentifiers: true
|
|
|
+ })
|
|
|
+ const node = root.children[0] as ElementNode
|
|
|
+ const props = ((node.codegenNode as CallExpression)
|
|
|
+ .arguments[1] as ObjectExpression).properties
|
|
|
+
|
|
|
+ expect(props[0]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: 'modelValue',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ content: '_ctx.model',
|
|
|
+ isStatic: false
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(props[1]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: 'onUpdate:modelValue',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ children: [
|
|
|
+ '$event => (',
|
|
|
+ {
|
|
|
+ content: '_ctx.model',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ ' = $event)'
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(generate(root, { mode: 'module' }).code).toMatchSnapshot()
|
|
|
+ })
|
|
|
+
|
|
|
+ test('compound expression', () => {
|
|
|
+ const root = parseWithVModel('<input v-model="model[index]" />')
|
|
|
+ const node = root.children[0] as ElementNode
|
|
|
+ const props = ((node.codegenNode as CallExpression)
|
|
|
+ .arguments[1] as ObjectExpression).properties
|
|
|
+
|
|
|
+ expect(props[0]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: 'modelValue',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ content: 'model[index]',
|
|
|
+ isStatic: false
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(props[1]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: 'onUpdate:modelValue',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ children: [
|
|
|
+ '$event => (',
|
|
|
+ {
|
|
|
+ content: 'model[index]',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ ' = $event)'
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(generate(root).code).toMatchSnapshot()
|
|
|
+ })
|
|
|
+
|
|
|
+ test('compound expression (with prefixIdentifiers)', () => {
|
|
|
+ const root = parseWithVModel('<input v-model="model[index]" />', {
|
|
|
+ prefixIdentifiers: true
|
|
|
+ })
|
|
|
+ const node = root.children[0] as ElementNode
|
|
|
+ const props = ((node.codegenNode as CallExpression)
|
|
|
+ .arguments[1] as ObjectExpression).properties
|
|
|
+
|
|
|
+ expect(props[0]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: 'modelValue',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ content: '_ctx.model',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ '[',
|
|
|
+ {
|
|
|
+ content: '_ctx.index',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ ']'
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(props[1]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: 'onUpdate:modelValue',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ children: [
|
|
|
+ '$event => (',
|
|
|
+ {
|
|
|
+ content: '_ctx.model',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ '[',
|
|
|
+ {
|
|
|
+ content: '_ctx.index',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ ']',
|
|
|
+ ' = $event)'
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(generate(root, { mode: 'module' }).code).toMatchSnapshot()
|
|
|
+ })
|
|
|
+
|
|
|
+ test('with argument', () => {
|
|
|
+ const root = parseWithVModel('<input v-model:value="model" />')
|
|
|
+ const node = root.children[0] as ElementNode
|
|
|
+ const props = ((node.codegenNode as CallExpression)
|
|
|
+ .arguments[1] as ObjectExpression).properties
|
|
|
+
|
|
|
+ expect(props[0]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: 'value',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ content: 'model',
|
|
|
+ isStatic: false
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(props[1]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: 'onUpdate:value',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ children: [
|
|
|
+ '$event => (',
|
|
|
+ {
|
|
|
+ content: 'model',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ ' = $event)'
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(generate(root).code).toMatchSnapshot()
|
|
|
+ })
|
|
|
+
|
|
|
+ test('with dynamic argument', () => {
|
|
|
+ const root = parseWithVModel('<input v-model:[value]="model" />')
|
|
|
+ const node = root.children[0] as ElementNode
|
|
|
+ const props = ((node.codegenNode as CallExpression)
|
|
|
+ .arguments[1] as ObjectExpression).properties
|
|
|
+
|
|
|
+ expect(props[0]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: 'value',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ content: 'model',
|
|
|
+ isStatic: false
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(props[1]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ content: 'onUpdate:',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ '+',
|
|
|
+ {
|
|
|
+ content: 'value',
|
|
|
+ isStatic: false
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ children: [
|
|
|
+ '$event => (',
|
|
|
+ {
|
|
|
+ content: 'model',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ ' = $event)'
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(generate(root).code).toMatchSnapshot()
|
|
|
+ })
|
|
|
+
|
|
|
+ test('with dynamic argument (with prefixIdentifiers)', () => {
|
|
|
+ const root = parseWithVModel('<input v-model:[value]="model" />', {
|
|
|
+ prefixIdentifiers: true
|
|
|
+ })
|
|
|
+ const node = root.children[0] as ElementNode
|
|
|
+ const props = ((node.codegenNode as CallExpression)
|
|
|
+ .arguments[1] as ObjectExpression).properties
|
|
|
+
|
|
|
+ expect(props[0]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ content: '_ctx.value',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ content: '_ctx.model',
|
|
|
+ isStatic: false
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(props[1]).toMatchObject({
|
|
|
+ key: {
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ content: 'onUpdate:',
|
|
|
+ isStatic: true
|
|
|
+ },
|
|
|
+ '+',
|
|
|
+ {
|
|
|
+ content: '_ctx.value',
|
|
|
+ isStatic: false
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ children: [
|
|
|
+ '$event => (',
|
|
|
+ {
|
|
|
+ content: '_ctx.model',
|
|
|
+ isStatic: false
|
|
|
+ },
|
|
|
+ ' = $event)'
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(generate(root, { mode: 'module' }).code).toMatchSnapshot()
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('errors', () => {
|
|
|
+ test('missing expression', () => {
|
|
|
+ const onError = jest.fn()
|
|
|
+ parseWithVModel('<span v-model />', { onError })
|
|
|
+
|
|
|
+ expect(onError).toHaveBeenCalledTimes(1)
|
|
|
+ expect(onError).toHaveBeenCalledWith(
|
|
|
+ expect.objectContaining({
|
|
|
+ code: ErrorCodes.X_V_MODEL_NO_EXPRESSION
|
|
|
+ })
|
|
|
+ )
|
|
|
+ })
|
|
|
+
|
|
|
+ test('empty expression', () => {
|
|
|
+ const onError = jest.fn()
|
|
|
+ parseWithVModel('<span v-model="" />', { onError })
|
|
|
+
|
|
|
+ expect(onError).toHaveBeenCalledTimes(1)
|
|
|
+ expect(onError).toHaveBeenCalledWith(
|
|
|
+ expect.objectContaining({
|
|
|
+ code: ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION
|
|
|
+ })
|
|
|
+ )
|
|
|
+ })
|
|
|
+ })
|
|
|
+})
|