Back to Repositories

Testing Vue Template Compilation and Code Generation in uni-app

This test suite validates the code generation functionality in uni-app’s compiler module, focusing on module and function mode preambles, text interpolation, and setup binding scenarios. The tests ensure proper transformation of Vue template syntax to WeChat Mini Program format.

Test Coverage Overview

Comprehensive test coverage spans multiple code generation scenarios:
  • Module mode preamble testing with various optimization flags
  • Function mode preamble validation with identifier prefixing
  • Static text and interpolation handling
  • Setup binding scenarios with literal constants
  • Edge cases including compound expressions and comment handling

Implementation Analysis

The testing approach employs Jest’s describe/test pattern for structured test organization. Each test case uses a custom assert utility to verify template transformation and code generation outcomes. The implementation specifically targets the conversion between Vue template syntax and WeChat Mini Program format, with careful attention to binding and rendering contexts.

Technical Details

Key technical components include:
  • Jest testing framework for test execution
  • Custom assert utility for validation
  • Vue compiler-core integration for binding types
  • Module and function mode configurations
  • Template transformation assertions

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Isolated test cases with clear input/output expectations
  • Comprehensive coverage of different compilation modes
  • Consistent test structure and naming conventions
  • Explicit configuration testing
  • Edge case handling and validation

dcloudio/uni-app

packages/uni-mp-compiler/__tests__/codegen.spec.ts

            
import { BindingTypes } from '@vue/compiler-core'
import { assert } from './testUtils'

describe('compiler: codegen', () => {
  test('module mode preamble', () => {
    assert(
      `<view v-for="item in items" @click="onClick"></view>`,
      `<view wx:for="{{a}}" wx:for-item="item" bindtap="{{b}}"></view>`,
      `import { o as _o, f as _f } from "vue"

export function render(_ctx, _cache) {
  return { a: _f(_ctx.items, (item, k0, i0) => { return {}; }), b: _o(_ctx.onClick) }
}`,
      { inline: false, mode: 'module', prefixIdentifiers: false }
    )
  })

  test('module mode preamble w/ optimizeImports: true', () => {
    assert(
      `<view v-for="item in items" @click="onClick"></view>`,
      `<view wx:for="{{a}}" wx:for-item="item" bindtap="{{b}}"></view>`,
      `import { o as _o, f as _f } from "vue"

export function render(_ctx, _cache) {
  return { a: _f(_ctx.items, (item, k0, i0) => { return {}; }), b: _o(_ctx.onClick) }
}`,
      { inline: false, mode: 'module' }
    )
  })

  test('function mode preamble', () => {
    assert(
      `<view v-for="item in items" @click="onClick"></view>`,
      `<view wx:for="{{a}}" wx:for-item="item" bindtap="{{b}}"></view>`,
      `const _Vue = Vue

return function render(_ctx, _cache) {
  with (_ctx) {
    const { o: _o, f: _f } = _Vue

    return { a: _f(items, (item, k0, i0) => { return {}; }), b: _o(onClick) }
  }
}`,
      { inline: false, mode: 'function', prefixIdentifiers: false }
    )
  })
  test('function mode preamble w/ prefixIdentifiers: true', () => {
    assert(
      `<view v-for="item in items" @click="onClick"></view>`,
      `<view wx:for="{{a}}" wx:for-item="item" bindtap="{{b}}"></view>`,
      `const { o: _o, f: _f } = Vue

return function render(_ctx, _cache) {
  return { a: _f(_ctx.items, (item, k0, i0) => { return {}; }), b: _o(_ctx.onClick) }
}`,
      { inline: false, mode: 'function' }
    )
  })
  test('static text', () => {
    assert(
      `hello`,
      `hello`,
      `(_ctx, _cache) => {
  return {}
}`
    )
  })
  test('interpolation', () => {
    assert(
      `{{hello}}`,
      `{{a}}`,
      `(_ctx, _cache) => {
  return { a: _t(_ctx.hello) }
}`
    )
  })
  test('comment', () => {
    assert(
      `<!--foo-->`,
      ``,
      `(_ctx, _cache) => {
  return {}
}`
    )
  })
  test('compound expression', () => {
    assert(
      `{{foo}}{{bar}}nested`,
      `{{a}}{{b}}nested`,
      `(_ctx, _cache) => {
  return { a: _t(_ctx.foo), b: _t(_ctx.bar) }
}`
    )
  })
  test('setup with literal-const', () => {
    assert(
      `{{add(count)}}`,
      `{{a}}`,
      `import { toDisplayString as _toDisplayString, t as _t } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return { a: _t($setup.add($setup.count)) }
}`,
      {
        inline: false,
        bindingMetadata: {
          add: BindingTypes.SETUP_CONST,
          count: BindingTypes.LITERAL_CONST,
        },
      }
    )
  })

  test('setup with x', () => {
    assert(
      `<view/>`,
      `<view/>`,
      `(_ctx, _cache) => {
  const __returned__ = {}
  return __returned__
}`,
      { inline: true, mode: 'module', prefixIdentifiers: false, isX: true }
    )
  })
})